Briefly
In most of these tutorials you will spare no effort to describe how to use the database. Today we are not looking at the database, but rather to focus on another feature that is important in Web applications: How to push messages to users.
In a lightweight application, we might add a mail service feature like this: When the user has a new fan, we send an email to the user. There are many ways to implement this feature, and we want to provide a reusable common framework to deal with.
Flask-mail Introduction
Fortunately for us, there are already a lot of external plug-ins to deal with the mail, although not hundred percent in accordance with our ideas to deal with, but has been quite close.
Installing Flask-mail in a virtual environment is fairly straightforward. Users other than Windows can use the following command to install:
Flask/bin/pip Install Flask-mail
Windows users have a slightly different installation, because some of the modules used by Flask-mail can no longer run on Windows systems, you may use the following command:
Flask\scripts\pip Install--no-deps Lamson chardet flask-mail
Configuration:
Looking back at the case in the Unit Test section of the previous article, we supported one such feature by adding a configuration: You can email us when a test of a version of the application is wrong. From this example you can see how to configure the use of mail support.
To remind you again, we need to set up two aspects of the content:
- Mail server information
- User Email Address
The following is the configuration used in the previous article
# email server
mail_server = ' your.mailserver.com '
mail_port =
Mail_use_tls = False
Mail_use_ssl = False
mail_username = ' you '
mail_password = ' Your-password '
# Administrator list
ADMINS = [' You@example.com ']
There are no actual mail servers and mailboxes set up for use. Now let's take an example to see how to use a Gmail email account to send mail:
# email server
mail_server = ' smtp.googlemail.com '
mail_port = 465
mail_use_tls = False
mail_use_ SSL = True
mail_username = ' your-gmail-username '
mail_password = ' Your-gmail-password '
# Administrator List
ADMINS = [' your-gmail-username@gmail.com ']
Alternatively, we can initialize a mail object to connect to the SMTP mail server and send the message:
From flask.ext.mail import mail
= mail (APP)
Send an email to try!
To find out how Flask-mail works, we can send an email from the command line to see. Go to the Python shell and execute the following script:
>>> from flask.ext.mail Import message
>>> to app import mail
>>> from config import ADMINS
>>> msg = message (' Test subject ', sender = Admins[0], recipients = ADMINS)
>>> msg.body = ' text body '
>>> msg.html = ' <b>HTML</b> body '
>>> mail.send (msg)
The above code sends an e-mail message to all mailboxes as the sender of the first mailbox, based on the list of mailbox addresses configured in inconfig.py. The message content is rendered in text and HTML two formats, and you can see which format depends on your mail client.
How simple and small! You can fully integrate it into your application right now.
Message Frame
We can now write a help function to send the message. This is the test of a generic version of the above test. We put this function into a new original file as a mail support (fileapp/emails.py):
From flask.ext.mail import message from
app import Mail
def send_email (subject, sender, recipients, Text_body, HT Ml_body):
msg = message (subject, sender, recipients)
msg.body = text_body msg.html
= Html_body
Mail.send (msg)
Flask-mail Mail support is beyond our current usage, and the functionality of BCC and accessory is not available in this application.
Follower Reminder
Now that we have the basic framework for sending emails, we can write a function that sends follower reminders (fileapp/emails.py):
From flask import render_template
from config import ADMINS
def follower_notification (followed, follower): C12/>send_email ("[microblog]%s is now following you!"% Follower.nickname,
admins[0],
[Followed.email],
render_template ("Follower_email.txt",
user = followed, follower = follower),
render_template (" Follower_email.html ",
user = followed, follower = follower))
Have you found any surprises here? Our old friend the Render_template function came up once.
If you remember, we use this function in the views rendering template. Just like in the views of writing HTML is not good, using the mail template is the ideal choice. We want to be able to separate the logic from the performance, so the email template will be placed in the template folder with other attempts to template.
So we need to write plain text and a web version of the mail template for follower reminders, and here's the plain text version (Fileapp/templates/follower_email.txt):
Dear {{User.nickname}},
{{follower.nickname}}} is now a follower. Click on the following link to visit {follower.nickname}} ' s profile page:
{url_for (' user ', nickname = Follower.nickn Ame, _external = True)}
regards, the
microblog admin
The following is a web version of the message, the effect will be better (fileapp/templates/follower_email.html):
<p>dear {{user.nickname}},</p>
<p><a href= "{{url_for (" user ", nickname = Follower.nickname, _external = True)} ' >{{follower.nickname}}</a> is now follower.</p>
<table>
<tr valign= "Top" >
<td></td>
<td>
<a Href= ' {{url_for (' user ', nickname = follower.nickname, _external = True)}} ' >{{FOLLOWER.NICKNAME}}</A><BR/ >
{follower.about_me}}
</td>
</tr>
</table>
<p>regards,</ p>
<p>the <code>microblog</code> admin</p>
Note: The meaning of the _external = True parameter of the Url_for function in the stencil. By default, the Url_for function generates URLs that are relative to our domain name. For example, the return value of the Url_for ("index") function is/index, but the message is that we want to http://localhost:5000/index this url,email there is no domain context, so we have to force a URL with a domain name, _external argument is the job.
The final step is to handle the "follow" process, which is the view function that triggers the message reminder (fileapp/views.py):
From emails import follower_notification
@app. Route ('/follow/<nickname> ')
@login_required
def Follow (nickname):
user = User.query.filter_by (nickname = nickname).
Follower_notification (user, G.user) return
redirect (url_for (' user ', nickname = nickname))
Now you can create two users (if you don't have one) and try to follow a user to another user and understand how email reminders work.
Is that it? Have we finished yet?
We may be very excited about this. Complete the work and delete the email reminder function with the unfinished list.
However, if you test the application now, you will find that when you click on the follow link, the page will be 2-3 seconds to respond, the browser will refresh, which is not before.
What happened?
The problem is that Flask-mail uses synchronous mode to send e-mail. Start with an e-mail message until the e-mail is delivered, and send back its response to the browser, blocking the Web server throughout the process. If we try to send email to a server that is slow, or even worse, temporarily offline, can you imagine what will happen? It's not good.
This is a terrible limitation, sending emails should be background tasks and will not interfere with the Web server, let's see how we can solve this problem.
Executing an asynchronous call in Python
We want the Send_email function to return immediately after the message is sent, requiring that the outgoing message be moved to the background process to execute asynchronously.
In fact Python has already provided support for asynchronous tasks, but in fact it can be done in other ways, such as threading and multi-process modules, as well as asynchronous tasks.
Whenever we need to send a message, it's more economical to start a thread to handle it than to start a whole new process. So let's put the Mail.send (msg) call into another thread. (fileapp/emails.py):
From threading import Thread
def send_async_email (msg):
mail.send (msg)
def send_email (subject, sender, Recipients, Text_body, html_body):
msg = message (subject, sender = sender, recipients = recipients)
Msg.body = Tex T_body
msg.html = html_body
thr = threading. Thread (target = send_async_email, args = [msg])
Thr.start ()
If you test the ' follow ' function, you will now find that the browser refreshes before sending the message.
So, we've implemented asynchronous delivery, but what if we need to implement it again in the future where we need asynchronous functionality?
The process is the same, so there will be duplicate code in each case, which is very bad.
We can improve the code by decorator. The code for using adorners is this:
From decorators import Async
@async
def send_async_email (msg):
mail.send (msg)
def send_email ( Subject, sender, recipients, Text_body, html_body):
msg = message (subject, sender = sender, recipients = recipients)
msg.body = text_body
msg.html = html_body
send_async_email (msg)
Better, isn't it?
The code that implements this approach is actually very simple, creating a source file (fileapp/decorators.py):
From threading import Thread
def async (f):
def wrapper (*args, **kwargs):
thr = Thread (target = f, args = AR GS, Kwargs = Kwargs)
thr.start () return
wrapper
Now we have created a useful framework for the asynchronous task, which we can say is done!
Just as an exercise, let's think about why this method looks like it's using a process rather than a thread. We don't want a process to be started whenever we need to send an email, so we can use Thepoolclass instead of themultiprocessingmodule. This class creates a specified number of processes (these are child processes of the main process), and these subprocess are sent through Theapply_asyncmethod to the process pool, waiting to be accepted for work. This may be an interesting way for a busy web site, but we will still maintain the current threading approach.
Last Words
The source code for this updatedmicroblogapplication is as follows:
Download Microblog-0.11.zip.