preface:
When we don't have a special background management platform, many types of applications need to remind administrators when specific events occur in the program, and the common communication method is e-mail, which is also the most stable way. The SMTP lib package in the Python standard library can be used to send e-mail in the flash program, but the flash mail extension wrapped with SMTP lib can be better integrated with flash.
1, Providing e-mail support using flash mail
pip instruction installation:
pip install flask-mail
Installation in pycharm:
Search and install flash mail:
Flash mail connects to the Simple Mail Transfer Protocol (SMTP) server and sends mail to the server. If not configured, flash mail will connect to port 25 on localhost and send e-mail without authentication. The following table lists the configurations that can be used to set up the SMTP server:
The following describes how to configure the program for sending mail:
Note: never write the account password and key information directly into the script, especially when you plan to open source your own works. In order to protect the account information, you need to let the script import sensitive information from the environment.
If we import a program from the local environment, we can set it as follows:
Using bash in Mac OS X, you can set these two variables as follows:
(venv) $ export MAIL_USERNAME=<Email account name> (venv) $ export MAIL_PASSWORD=<Mailbox server key>
Microsoft Windows users can set environment variables as follows:
(venv) $ set MAIL_USERNAME=<Email account name> (venv) $ set MAIL_PASSWORD=<Mailbox server key>
Note when setting local environment variables:
(1)MAIL_USERNAME or mail_ To the right of the password equal sign are strings corresponding to the content prompt, such as MAIL_PASSWORD=‘123’;
(2) The mailbox server key refers to the key applied in the corresponding mailbox (qq or 163) (like the activation code of Microsoft computer system). As for how to apply, a method link is released here: python - developing automatic mail sending program based on yagmail Library
, please read the mailbox setting steps in detail.
The following key information related to me is replaced by xxxx. Just fill in your own data according to the corresponding content requirements!
Configure flash mail in app.py:
import os app.config['MAIL_SERVER'] = 'smtp.163.com' app.config['MAIL_PORT'] = 465 app.config['MAIL_USE_TLS'] = False app.config['MAIL_USE_SSL'] = True app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME') app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')
Note: 'MAIL_USE_TLS' is set to False, 'mail'_ USE_ If SSL 'is set to True, the success rate of sending mail will be high. Otherwise, the default setting will generally lead to the error prompt of mailbox connection failure or positive rejection of the computer!
Initialize flash mail in app.py:
from flask.ext.mail import Mail mail = Mail(app)
However, the Microsoft system environment is unstable. It is not recommended for beginners to use local environment variables. In this way, many people may fail to read in the system. It is recommended to configure directly here!
In addition, mail instantiation must be configured. The following code demonstration of direct configuration will be provided!
Directly configure in app.py:
app.config['MAIL_SERVER'] = 'smtp.163.com' app.config['MAIL_PORT'] = 465 app.config['MAIL_USERNAME'] = "xxxx@163.com" app.config['MAIL_PASSWORD'] = "xxxx" #In order to protect personal privacy, replace it with x and fill it in according to the information you have obtained app.config['MAIL_USE_TLS'] = False app.config['MAIL_USE_SSL'] = True mail = Mail(app)#mail instantiation must be placed after configuration
2, Send email in Python shell
If flash is already installed_ Script, I won't talk about it here. See details for installation and use Flash Web development-1.4 web program and database processing The tenth integrated shell!
Then we can test whether our program can send an email correctly in the shell environment:
We can enter the following codes in turn:
(venv) $ python hello.py shell >>> from flask_mail import Message >>> from hello import mail >>> msg = Message('test subject', sender='sender @example.com',recipients=['Receiver@example.com']) >>> msg.body = 'text body' >>> msg.html = '<b>HTML</b> body' >>> with app.app_context(): ... mail.send(msg)
The operations in pycharm are as follows:
After testing, it can successfully receive the mail and the configuration is correct!
Similarly, in app.py, add:
with app.app_context(): message = Message(subject='hello flask-mail', sender="xxxx@163.com", recipients=['xxxx@qq.com'], body='Test mail') mail.send(message)
Note that the send() function in flask mail uses current_app, so it should be executed in the context of the activated program.
3, Integrate the function of sending e-mail in the program
In order to avoid writing e-mail messages manually every time, we'd better abstract the general part of the program sending e-mail and define it as a function. In addition, this has the advantage that the function can render the mail body using the Jinja2 template, which is very flexible.
Use functions in app.py to implement:
from flask_mail import Mail,Message app.config['FLASKY_MAIL_SUBJECT_PREFIX'] = '[Flasky]' app.config['FLASKY_MAIL_SENDER'] = 'xxxx@163.com' def send_email(to, subject, template, **kwargs): msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject,sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to]) msg.body = render_template(template + '.txt', **kwargs) msg.html = render_template(template + '.html', **kwargs) mail.send(msg)
This send_ The email function uses two program specific configuration items to define the prefix 'flag' of the email subject_ MAIL_ SUBJECT_ Prefix 'and the address of the sender [' flag_mail_sender ']. send_ The parameters of the email function are the recipient address, subject, template for rendering the mail body and keyword parameter list (keyword parameters are mainly used to pass in template variables here). The extension cannot be included when specifying the template, so that the two templates can be used to render plain text body (txt) and rich text body (html and other files) respectively. The caller passes the keyword parameter to render_template() function to use in the template to generate the e-mail body.
The index() view function is easily extended so that whenever the form receives a new name (that is, a new user accesses and logs in), the program sends an email to the administrator.
In app.py, the index function is written as follows:
app.config['FLASKY_ADMIN'] = "xxxxx@163.com" @app.route('/', methods=['GET', 'POST']) def index(): form = NameForm() if form.validate_on_submit(): user = User.query.filter_by(username=form.name.data).first() if user is None: user = User(username = form.name.data) db.session.add(user) session['known'] = False if app.config['FLASKY_ADMIN']: send_emails(app.config['FLASKY_ADMIN'], 'new user','mailnew_user', user=user) else: session['known'] = True session['name'] = form.name.data form.name.data = '' return redirect(url_for('index')) return render_template('home.html',form = form, name = session.get('name'),known = session.get('known', False))
The recipient of the e-mail is saved in the environment variable flasky_ In admin, it will be loaded into a configuration variable with the same name during program startup. We will create two template files to render the message body in plain text and HTML respectively. Both template files are saved in the templates folder. In the template of mail e-mail, one of the template parameters is user, so send is called_ Mail() function is passed in as a keyword parameter to the user.
Other environment variable settings:
set name=<String form corresponding to content>
We configure, set and write the program code. If we succeed, the manager (receiver) will receive an email prompt every time we log in a new user on the login page!
4, Send email asynchronously
If you send e-mail as above, the mail.send() function will stop for a few seconds when sending e-mail. In order to avoid unnecessary delay in processing requests, we can move the function of sending e-mail to the background thread and use the relevant contents of the threading library. Note: threading is a local library and does not need to be downloaded and installed separately!.
In app.py, we can make the following changes:
from threading import Thread def send_async_email(app, msg): with app.app_context(): mail.send(msg) def send_email(to, subject, template, **kwargs): msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject, sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to]) msg.body = render_template(template + '.txt', **kwargs) msg.html = render_template(template + '.html', **kwargs) thr = Thread(target=send_async_email, args=[app, msg]) thr.start() return thr
Open the mailbox to be accepted in advance. We can find that sending mail with a dedicated thread is faster than sending mail without a dedicated thread!, This is the excellent effect of special threads! Remember, however, that when a program sends a large number of e-mails, it is more appropriate to use a job dedicated to sending e-mails than to create a new thread for each message. For example, we can execute send_ async_ The operation of email() function is sent to Celery( http://www.celeryproject.org/ )Task queue.
If you want to save the functions without threads and with threads at the same time, you can wait for another use. Just distinguish the function name with threads from the function name without threads, divide it into two different functions (in fact, the main parameters are similar), and then decide whether to use the function with threads in the index function!
The above implementation involves an interesting problem. Many flash extensions assume that the active program context and request context already exist. The send() function in Flask mail uses current_app, so the program context must be activated. However, when executing the mail.send() function in different threads, the program context uses app.app_context() is created manually.
5, Summary of possible problems and solutions in development
pip command failed
Using Python flash_ Script.py -- help error: ModuleNotFoundError:no module named 'flash_ compat‘
6, Related code module
Only the parts that have been changed to the modules earlier in this column are shown, and other defaults remain unchanged.
1.mailnew_user.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>{{ user }}Log in to your website!</h1> </body> </html>
Note: mailnew_user.txt is a temporarily empty TXT text file
2.app.py
from flask import Flask, render_template,url_for,redirect,session,flash from flask_bootstrap import Bootstrap from flask_moment import Moment from datetime import datetime from flask_wtf import FlaskForm from wtforms import StringField, SubmitField from wtforms.validators import Required from flask_sqlalchemy import SQLAlchemy from flask_script import Shell,Manager from flask_migrate import Migrate,MigrateCommand from flask_mail import Mail,Message from threading import Thread import pymysql #Program initial configuration pymysql.install_as_MySQLdb() app = Flask(__name__) bootstrap = Bootstrap(app) moment = Moment(app) manager = Manager(app) def make_shell_context(): return dict(app=app, db=db, User=User, Role=Role) manager.add_command("shell", Shell(make_context=make_shell_context)) #Database connection configuration app.config['SECRET_KEY'] = 'hard to guess string' app.config['SQLALCHEMY_DATABASE_URI'] ='mysql://root:xxxxx@localhost/me' app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True db = SQLAlchemy(app) migrate = Migrate(app, db) manager.add_command('db', MigrateCommand) #Mailbox configuration app.config['MAIL_SERVER'] = 'smtp.163.com' app.config['MAIL_PORT'] = 465 app.config['MAIL_USERNAME'] = "xxxxx@163.com" app.config['MAIL_PASSWORD'] = "xxxxx" app.config['MAIL_USE_TLS'] = False app.config['MAIL_USE_SSL'] = True mail = Mail(app) #The following is the code used to test whether sending a single message is successful. The completed messages can be deleted or hidden '''with app.app_context(): message = Message(subject='hello flask-mail', sender="xxxxx@163.com", recipients=['xxxxx@qq.com'], body='Test mail') mail.send(message)''' app.config['FLASKY_MAIL_SUBJECT_PREFIX'] = '[Flasky]' app.config['FLASKY_MAIL_SENDER'] = 'xxxxxxx@163.com' app.config['FLASKY_ADMIN'] = "xxxxx@163.com" def send_email(to, subject, template, **kwargs): msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject,sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to]) msg.body = render_template(template + '.txt', **kwargs) msg.html = render_template(template + '.html', **kwargs) mail.send(msg) def send_async_email(app, msg): with app.app_context(): mail.send(msg) def send_emails(to, subject, template, **kwargs): msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject, sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to]) msg.body = render_template(template + '.txt', **kwargs) msg.html = render_template(template + '.html', **kwargs) thr = Thread(target=send_async_email, args=[app, msg]) thr.start() return thr class NameForm(FlaskForm): name = StringField('What's your name??', validators=[Required()]) submit = SubmitField('Submit') #Database model definition class Role(db.Model): __tablename__ = 'roles' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True) users = db.relationship('User', backref='role', lazy='dynamic') def __repr__(self): return '<Role %r>' % self.name class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(64), unique=True, index=True) role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) def __repr__(self): return '<User %r>' % self.username @app.route('/', methods=['GET', 'POST']) def index(): form = NameForm() if form.validate_on_submit(): user = User.query.filter_by(username=form.name.data).first() if user is None: user = User(username = form.name.data) db.session.add(user) session['known'] = False if app.config['FLASKY_ADMIN']: send_emails(app.config['FLASKY_ADMIN'], 'new user','mailnew_user', user=user) else: session['known'] = True session['name'] = form.name.data form.name.data = '' return redirect(url_for('index')) return render_template('home.html',form = form, name = session.get('name'),known = session.get('known', False)) @app.route('/user/<name>') def users(name): return render_template('user.html',name=name,current_time=datetime.utcnow()) @app.errorhandler(404) def page_not_found(e): return render_template('404.html',current_time=datetime.utcnow()),404 @app.errorhandler(500) def internal_error(e): return render_template('500.html',current_time=datetime.utcnow()),500 @app.route('/form') def useform(): form=NameForm() return render_template('FORM.html',form=form) if __name__ == '__main__': manager.run()
Finally, if there are deficiencies in the article, criticism and correction are welcome!