Skip to content

Commit d54d3c9

Browse files
committed
Refactored email code to use python-rq and the (new) send_async_email function
1 parent 23ca0a6 commit d54d3c9

File tree

11 files changed

+90
-57
lines changed

11 files changed

+90
-57
lines changed

app/account/views.py

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
)
88
from . import account
99
from .. import db
10-
from ..email import send_email
10+
from ..email import send_async_email
1111
from ..models import User
1212
from .forms import (
1313
LoginForm,
@@ -18,6 +18,7 @@
1818
RequestResetPasswordForm,
1919
ResetPasswordForm
2020
)
21+
from app import redis_queue
2122

2223

2324
@account.route('/login', methods=['GET', 'POST'])
@@ -26,7 +27,8 @@ def login():
2627
form = LoginForm()
2728
if form.validate_on_submit():
2829
user = User.query.filter_by(email=form.email.data).first()
29-
if user is not None and user.verify_password(form.password.data):
30+
if user is not None and user.password_hash is not None and \
31+
user.verify_password(form.password.data):
3032
login_user(user, form.remember_me.data)
3133
flash('You are now logged in. Welcome back!', 'success')
3234
return redirect(request.args.get('next') or url_for('main.index'))
@@ -47,8 +49,15 @@ def register():
4749
db.session.add(user)
4850
db.session.commit()
4951
token = user.generate_confirmation_token()
50-
send_email(user.email, 'Confirm Your Account',
51-
'account/email/confirm', user=user, token=token)
52+
confirm_link = url_for('account.confirm', token=token, _external=True)
53+
redis_queue.enqueue(
54+
send_async_email,
55+
recipient=user.email,
56+
subject='Confirm Your Account',
57+
template='account/email/confirm',
58+
user=user,
59+
confirm_link=confirm_link
60+
)
5261
flash('A confirmation link has been sent to {}.'.format(user.email),
5362
'warning')
5463
return redirect(url_for('main.index'))
@@ -81,12 +90,17 @@ def reset_password_request():
8190
user = User.query.filter_by(email=form.email.data).first()
8291
if user:
8392
token = user.generate_password_reset_token()
84-
send_email(user.email,
85-
'Reset Your Password',
86-
'account/email/reset_password',
87-
user=user,
88-
token=token,
89-
next=request.args.get('next'))
93+
reset_link = url_for('account.reset_password', token=token,
94+
_external=True)
95+
redis_queue.enqueue(
96+
send_async_email,
97+
recipient=user.email,
98+
subject='Reset Your Password',
99+
template='account/email/reset_password',
100+
user=user,
101+
reset_link=reset_link,
102+
next=request.args.get('next')
103+
)
90104
flash('A password reset link has been sent to {}.'
91105
.format(form.email.data),
92106
'warning')
@@ -141,11 +155,18 @@ def change_email_request():
141155
if current_user.verify_password(form.password.data):
142156
new_email = form.email.data
143157
token = current_user.generate_email_change_token(new_email)
144-
send_email(new_email,
145-
'Confirm Your New Email',
146-
'account/email/change_email',
147-
user=current_user,
148-
token=token)
158+
change_email_link = url_for('account.change_email', token=token,
159+
_external=True)
160+
redis_queue.enqueue(
161+
send_async_email,
162+
recipient=new_email,
163+
subject='Confirm Your New Email',
164+
template='account/email/change_email',
165+
# current_user is a LocalProxy, we want the underlying user
166+
# object
167+
user=current_user._get_current_object(),
168+
change_email_link=change_email_link
169+
)
149170
flash('A confirmation link has been sent to {}.'.format(new_email),
150171
'warning')
151172
return redirect(url_for('main.index'))
@@ -170,8 +191,16 @@ def change_email(token):
170191
def confirm_request():
171192
"""Respond to new user's request to confirm their account."""
172193
token = current_user.generate_confirmation_token()
173-
send_email(current_user.email, 'Confirm Your Account',
174-
'account/email/confirm', user=current_user, token=token)
194+
confirm_link = url_for('account.confirm', token=token, _external=True)
195+
redis_queue.enqueue(
196+
send_async_email,
197+
recipient=current_user.email,
198+
subject='Confirm Your Account',
199+
template='account/email/confirm',
200+
# current_user is a LocalProxy, we want the underlying user object
201+
user=current_user._get_current_object(),
202+
confirm_link=confirm_link
203+
)
175204
flash('A new confirmation link has been sent to {}.'.
176205
format(current_user.email),
177206
'warning')
@@ -225,12 +254,16 @@ def join_from_invite(user_id, token):
225254
flash('The confirmation link is invalid or has expired. Another '
226255
'invite email with a new link has been sent to you.', 'error')
227256
token = new_user.generate_confirmation_token()
228-
send_email(new_user.email,
229-
'You Are Invited To Join',
230-
'account/email/invite',
231-
user=new_user,
232-
user_id=new_user.id,
233-
token=token)
257+
invite_link = url_for('account.join_from_invite', user_id=user_id,
258+
token=token, _external=True)
259+
redis_queue.enqueue(
260+
send_async_email,
261+
recipient=new_user.email,
262+
subject='You Are Invited To Join',
263+
template='account/email/invite',
264+
user=new_user,
265+
invite_link=invite_link
266+
)
234267
return redirect(url_for('main.index'))
235268

236269

app/admin/views.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
from . import admin
1313
from ..models import User, Role
1414
from .. import db
15-
from ..email import send_email
15+
from ..email import send_async_email
16+
from app import redis_queue
1617

1718

1819
@admin.route('/')
@@ -56,12 +57,16 @@ def invite_user():
5657
db.session.add(user)
5758
db.session.commit()
5859
token = user.generate_confirmation_token()
59-
send_email(user.email,
60-
'You Are Invited To Join',
61-
'account/email/invite',
62-
user=user,
63-
user_id=user.id,
64-
token=token)
60+
invite_link = url_for('account.join_from_invite', user_id=user.id,
61+
token=token, _external=True)
62+
redis_queue.enqueue(
63+
send_async_email,
64+
recipient=user.email,
65+
subject='You Are Invited To Join',
66+
template='account/email/invite',
67+
user=user,
68+
invite_link=invite_link,
69+
)
6570
flash('User {} successfully invited'.format(user.full_name()),
6671
'form-success')
6772
return render_template('admin/new_user.html', form=form)

app/email.py

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
1-
from threading import Thread
2-
3-
from flask import current_app, render_template
1+
import os
2+
from flask import render_template
43
from flask.ext.mail import Message
54
from . import mail
5+
from app import create_app
66

77

8-
def send_async_email(app, msg):
8+
def send_async_email(recipient, subject, template, **kwargs):
9+
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
910
with app.app_context():
11+
msg = Message(app.config['EMAIL_SUBJECT_PREFIX'] + ' ' + subject,
12+
sender=app.config['EMAIL_SENDER'],
13+
recipients=[recipient])
14+
msg.body = render_template(template + '.txt', **kwargs)
15+
msg.html = render_template(template + '.html', **kwargs)
1016
mail.send(msg)
11-
12-
13-
def send_email(to, subject, template, **kwargs):
14-
app = current_app._get_current_object()
15-
msg = Message(app.config['EMAIL_SUBJECT_PREFIX'] + ' ' + subject,
16-
sender=app.config['EMAIL_SENDER'], recipients=[to])
17-
msg.body = render_template(template + '.txt', **kwargs)
18-
msg.html = render_template(template + '.html', **kwargs)
19-
thr = Thread(target=send_async_email, args=[app, msg])
20-
thr.start()
21-
return thr

app/templates/account/email/change_email.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<p>Dear {{ user.full_name() }},</p>
22

3-
<p>To confirm your new email address <a href="{{ url_for('account.change_email', token=token, _external=True) }}">click here</a>.</p>
3+
<p>To confirm your new email address <a href="{{ change_email_link }}">click here</a>.</p>
44

55
<p>Alternatively, you can paste the following link in your browser's address bar:</p>
66

7-
<p>{{ url_for('account.change_email', token=token, _external=True) }}</p>
7+
<p>{{ change_email_link }}</p>
88

99
<p>Sincerely,</p>
1010

app/templates/account/email/change_email.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Dear {{ user.full_name() }},
22

33
To confirm your new email address click on the following link:
44

5-
{{ url_for('account.change_email', token=token, _external=True) }}
5+
{{ change_email_link }}
66

77
Sincerely,
88

app/templates/account/email/confirm.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
<p>Welcome to <b>{{ config.APP_NAME }}</b>!</p>
44

5-
<p>To confirm your account, please <a href="{{ url_for('account.confirm', token=token, _external=True) }}">click here</a>.</p>
5+
<p>To confirm your account, please <a href="{{ confirm_link }}">click here</a>.</p>
66

77
<p>Alternatively, you can paste the following link in your browser's address bar:</p>
88

9-
<p>{{ url_for('account.confirm', token=token, _external=True) }}</p>
9+
<p>{{ confirm_link }}</p>
1010

1111
<p>Sincerely,</p>
1212

app/templates/account/email/confirm.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Welcome to {{ config.APP_NAME }}!
44

55
To confirm your account, please click on the following link:
66

7-
{{ url_for('account.confirm', token=token, _external=True) }}
7+
{{ confirm_link }}
88

99
Sincerely,
1010

app/templates/account/email/invite.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
<p>You are invited to join <b>{{ config.APP_NAME }}</b>!</p>
44

5-
<p>To set your password, please <a href="{{ url_for('account.join_from_invite', user_id=user_id, token=token, _external=True) }}">click here</a>.</p>
5+
<p>To set your password, please <a href="{{ invite_link }}">click here</a>.</p>
66

77
<p>Alternatively, you can paste the following link in your browser's address bar:</p>
88

9-
<p>{{ url_for('account.join_from_invite', user_id=user_id, token=token, _external=True) }}</p>
9+
<p>{{ invite_link }}</p>
1010

1111
<p>Once completed, please log in as {{ user.email }} with the password you set.</p>
1212

app/templates/account/email/invite.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ You are invited to join {{ config.APP_NAME }}!
44

55
To set your password, please click on the following link:
66

7-
{{ url_for('account.join_from_invite', user_id=user_id, token=token, _external=True) }}
7+
{{ invite_link }}
88

99
Once completed, please log in as {{ user.email }} with the password you set.
1010

app/templates/account/email/reset_password.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<p>Dear {{ user.full_name() }},</p>
22

3-
<p>To reset your password, <a href="{{ url_for('account.reset_password', token=token, _external=True) }}">click here</a>.</p>
3+
<p>To reset your password, <a href="{{ reset_link }}">click here</a>.</p>
44

55
<p>Alternatively, you can paste the following link in your browser's address bar:</p>
66

7-
<p>{{ url_for('account.reset_password', token=token, _external=True) }}</p>
7+
<p>{{ reset_link }}</p>
88

99
<p>If you have not requested a password reset, simply ignore this message.</p>
1010

0 commit comments

Comments
 (0)