flask web 编程学习笔记5:E-Mail


Flask 可快速简单的实现E-Mail的功能,可以用于用户注册,找回密码等功能,且邮件内容灵活。
复制下述代码的内容,修改成自己的邮箱和一些配置文件就可以实现简单的邮件发送功能。

'''
    Note5. E-Mail And Example
'''
#
#########################################################################
#
#     E-Mail 由flask-mail提供支持,若不验证,Flask-Mail 会连接localhost 
# 上的端口25,无需验证即可发送电子邮件。
#     安装方法:pip install flask-mail
#
#########################################################################
#
import os
from flask import Flask
#
from flask.ext.mail import Mail, Message
#
from threading import Thread
#
app = Flask(__name__)
mail = Mail(app)
#
# 若配置外部邮箱使用如下方式:
#
app.config['MAIL_SERVER'] = 'xxx.mxhichina.com'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
#
# windows使用如下方式设置环境变量即可:
# (venv) $ set MAIL_USERNAME=
# (venv) $ set MAIL_PASSWORD=
#
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
#
# Linux使用如下方式设置环境变量即可:
# (venv) $ export MAIL_USERNAME=
# (venv) $ export MAIL_PASSWORD=
#
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')
#
# Flask-Mail SMTP服务器的配置:
# |--------------------------------------------------------------------------------
# | 配  置         默认值         说  明
# |--------------------------------------------------------------------------------
# | MAIL_SERVER     localhost   电子邮件服务器的主机名或IP 地址
# | MAIL_PORT       25          电子邮件服务器的端口
# | MAIL_USE_TLS    False       启用传输层安全(Transport Layer Security,TLS)协议
# | MAIL_USE_SSL    False       启用安全套接层(Secure Sockets Layer,SSL)协议
# | MAIL_USERNAME   None        邮件账户的用户名
# | MAIL_PASSWORD   None        邮件账户的密码
# | --------------------------------------------------------------------------------
#
# 
app.config['FLASKY_MAIL_SUBJECT_PREFIX'] = '[Flasky]'
#
app.config['FLASKY_MAIL_SENDER'] = 'Flasky Admin <flasky@example.com>'
# ...
app.config['FLASKY_ADMIN'] = os.environ.get('FLASKY_ADMIN')
# ...

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 thr_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
    
    
@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_email(app.config['FLASKY_ADMIN'], 'New User',\
                           'mail/new_user', user=user)
        else:
            session['known'] = True
        session['name'] = form.name.data
        form.name.data = ''
        return redirect(url_for('index'))
    return render_template('index.html',\ 
                           form=form,\ 
                           name=session.get('name'),\
                           known=session.get('known', False))        

if __name__ == '__main__':
    app.run(debug=True)

flask web 编程学习笔记4:SQL


Flask 支持多种数据库,并采用sqlalchemy 作为常用ORM,链接数据库也比较方便。
1. 复制执行下述代码,即可实现数据库的链接。
2. 用编辑器打开数据库文件,可以看到自己添加的数据内容。

'''
    Note4. SQL Database And Example
'''
#
#########################################################################
#
#     数据库按照一定规则保存程序数据,程序再发起查询取回所需
# 的数据。Web 程序最常用基于关系模型的数据库,这种数据库也称
# 为SQL 数据库,因为它们使用结构化查询语言。不过最近几年文档
# 数据库和键值对数据库成了流行的替代选择,这两种数据库合称
# NoSQL数据库。
# 
#########################################################################
#
# 1) Flask 可用MySQL、Postgres、SQLite、Redis、MongoDB或者CouchDB。
#    SQLAlchemy ORM 作为一个数据库工具,常被用于项目。
#
# 2) SQLAlchemy安装: pip install flask-sqlalchemy.
#
# 3) SQLAlchemy支持以下数据库,方法如下:
# |----------------------------------------------------------------------
# | 数据库            | 引擎URL
# |----------------------------------------------------------------------
# | MySQL             | mysql://username:password@hostname/database
# | Postgres          | postgresql://username:password@hostname/database
# | SQLite(Unix)    | sqlite:////absolute/path/to/database
# | SQLite(Windows) | sqlite:///c:/absolute/path/to/database
# |----------------------------------------------------------------------
# 
# 以下按照sqlite3 进行说明。
#
from flask import Flask,render_template, session, redirect, url_for
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.bootstrap import Bootstrap
from flask.ext.wtf import Form
from wtforms import StringField, SubmitField
from wtforms.validators import Required
import os

# 得到项目路径的常用方法。
basedir = os.path.abspath(os.path.dirname(__file__))

# Flask 实例化
app = Flask(__name__)
# 定义app数据库文件及路径。
app.config['SQLALCHEMY_DATABASE_URI'] =\
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
# SQLALCHEMY_COMMIT_ON_TEARDOWN配置。
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
#
app.config['SECRET_KEY'] = 'hard to guess string'
# db 对象是SQLAlchemy 类的实例,表示程序使用的数据库,同时还获得了
# Flask-SQLAlchemy提供的所有功能。
db = SQLAlchemy(app)
#
bootstrap = Bootstrap(app)
#
#     采用类继承的方式定义新的数据库表格,类变量__tablename__ 定义在
# 数据库中使用的表名。Column函数定义表中元素,其参数特点如下:
#     db.Column(类型,其他参数,....)
#
# 1) 类型表.
# |-------------------------------------------------------------------------
# | 类型名       | Python类型          | 说  明
# |-------------------------------------------------------------------------
# | Integer      | int                 | 普通整数,一般是32 位
# | SmallInteger | int                 | 取值范围小的整数,一般是16 位
# | BigInteger   | int 或long          | 不限制精度的整数
# | Float        | float               | 浮点数
# | Numeric      | decimal.Decimal     | 定点数
# | String       | str                 | 变长字符串
# | Text         | str                 | 变长字符串,对较长或不限长度的字符串
# |------------------------------------| 做了优化
# | Unicode      | unicode             | 变长Unicode 字符串
# | UnicodeText  | unicode             | 变长Unicode 字符串,对较长或不限长度
# |------------------------------------| 的字符串做了优化
# | Boolean      | bool                | 布尔值
# | Date         | datetime.date       | 日期
# | Time         | datetime.time       | 时间
# | DateTime     | datetime.datetime   | 日期和时间
# | Interval     | datetime.timedelta  | 时间间隔
# | Enum         | str                 | 一组字符串
# | PickleType   | 任何Python          | 对象自动使用Pickle 序列化
# | LargeBinary  | str                 | 二进制文件
# |---------------------------------------------------------------------------
#
# 2) 其他属性表
# |---------------------------------------------------------------------------
# | 选项名      | 说  明
# |---------------------------------------------------------------------------
# | primary_key | 如果设为True,这列就是表的主键
# | unique      | 如果设为True,这列不允许出现重复的值
# | index       | 如果设为True,为这列创建索引,提升查询效率
# | nullable    | 如果设为True,这列允许使用空值;如果设为False,这列不允许使用
# |-------------| 空值
# | default     | 为这列定义默认值
# |---------------------------------------------------------------------------
#
class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    #     db.relationship() 中的backref 参数向User 模型中添加一个role 属性,
    # 从而定义反向关系。这一属性可替代role_id 访问Role 模型,此时获取的是模
    # 型对象,而不是外键的值。
    users = db.relationship('User', backref='role')
    def __repr__(self):
        return '' % 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)
    #     关系使用users 表中的外键连接了两行。添加到User 模型中的role_id 列
    # 被定义为外键,就是这个外键建立起了关系。传给db.ForeignKey() 的参数
    # 'roles.id' 表明,这列的值是roles 表中行的id 值。
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    def __repr__(self):
        return '' % self.username

class NameForm(Form):
    name = StringField('What is your name?', validators=[Required()])
    submit = SubmitField('Submit')
    
@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
        else:
            session['known'] = True
        session['name'] = form.name.data
        form.name.data = ''
        return redirect(url_for('index'))
    return render_template('sql.html',\
                           form = form, name = session.get('name'),\
                           known = session.get('known', False))
if __name__ == '__main__':
    db.create_all()
    app.run(debug=True)

flask web 编程学习笔记3:表单


表单作为一个常用的html选项,flask使用flask-WTF实现了常用的表单功能,实现简单方便。

复制代码,并执行,可以在浏览器中看到输入框。

'''
    Note3. WTF And Example
'''
#
#########################################################################
#
#     使用Flask-WTF可以处理表单,按照如下方式进行安装:
# pip install flask-wtf
#
#########################################################################
#
from flask import Flask, render_template, session, redirect, url_for, flash
# wtf about import file
from flask.ext.wtf import Form
from wtforms import StringField, SubmitField
from wtforms.validators import Required
from flask.ext.bootstrap import Bootstrap
#
app = Flask(__name__)
#
# 1) Flask-WTF 能保护所有表单免受跨站请求伪造(Cross-Site Request 
# Forgery,CSRF)的攻击。恶意网站把请求发送到被攻击者已登录的其他网站时
# 就会引发CSRF 攻击。
# 2) 为了实现CSRF 保护,Flask-WTF 需要程序设置一个密钥。Flask-WTF使用
# 这个密钥生成加密令牌,再用令牌验证请求中表单数据的真伪。
#
app.config['SECRET_KEY'] = 'hard to guess string'
#
bootstrap = Bootstrap(app)
#
class NameForm(Form):
    #
    # 使用Flask-WTF 时,每个Web 表单都由一个继承自Form 的类表示。
    # NameForm 表单中有一个名为name的文本字段和一个名为submit的提交按钮。
    # Form 基类由Flask-WTF扩展定义,所以从flask.ext.wtf中导入。字段和验
    # 证函数却可以直接从WTForms 包中导入。
    #
    name = StringField('What is your name?', validators=[Required()])
    submit = SubmitField('Submit')
# 
# Form支持的字段:
# |----------------------------------------------------------------------
# | 字段类型            | 说  明
# |----------------------------------------------------------------------
# | StringField         | 文本字段
# | TextAreaField       | 多行文本字段
# | PasswordField       | 密码文本字段
# | HiddenField         | 隐藏文本字段
# | DateField           | 文本字段,值为datetime.date 格式
# | DateTimeField       | 文本字段,值为datetime.datetime 格式
# | IntegerField        | 文本字段,值为整数
# | DecimalField        | 文本字段,值为decimal.Decimal
# | FloatField          | 文本字段,值为浮点数
# | BooleanField        | 复选框,值为True 和False
# | RadioField          | 一组单选框
# | SelectField         | 下拉列表
# | SelectMultipleField | 下拉列表,可选择多个值
# | FileField           | 文件上传字段
# | SubmitField         | 表单提交按钮
# | FormField           | 把表单作为字段嵌入另一个表单
# | FieldList           | 一组指定类型的字段
# |------------------------------------------------------------------------
# WTForms 内建的验证函数:
# |------------------------------------------------------------------------
# | 验证函数        | 说  明
# |------------------------------------------------------------------------
# | Email           | 验证电子邮件地址
# | EqualTo         | 比较两个字段的值;常用于要求输入两次密码进行确认的情况
# | IPAddress       | 验证IPv4 网络地址
# | Length          | 验证输入字符串的长度
# | NumberRange     | 验证输入的值在数字范围内
# | Optional        | 无输入值时跳过其他验证函数
# | Required        | 确保字段中有数据
# | Regexp          | 使用正则表达式验证输入值
# | URL             | 验证URL
# | AnyOf           | 确保输入值在可选值列表中
# | NoneOf          | 确保输入值不在可选值列表中
# |------------------------------------------------------------------------
#


@app.route('/', methods=['GET', 'POST'])
def index():
    # 用户第一次访问程序时,服务器会收到一个没有表单数据的GET 请求,
    # 所以validate_on_submit() 将返回False。if 语句的内容将被跳过,
    # 通过渲染模板处理请求,并传入表单对象和值为None 的name 变量作
    # 为参数。用户会看到浏览器中显示了一个表单。
    name = None
    form = NameForm()
    # 提交表单后,如果数据能被所有验证函数接受,那么validate_on_submit() 
    # 方法的返回值为True,否则返回False。
    if form.validate_on_submit():
        name = form.name.data        
        form.name.data = ''        
    return render_template('wtf.html', form=form, name=name)
    
    '''
    or : 重定向的方式,采用session记录上下文。
        session['name'] = form.name.data
        return redirect(url_for('index'))
    return render_template('wtf.html', form=form, name=session.get('name'))
    
    or : flash 消息用来提示客户状态值。
        if old_name is not None and old_name != form.name.data:
            flash('Looks like you have changed your name!')
            session['name'] = form.name.data
            return redirect(url_for('index'))
    return render_template('index.html',\
                           form = form,\
                           name = session.get('name'))
    '''
# 注,传递flash消息时,需要修改html,增加:{% for message in get_flashed_messages() %}    
    
if __name__ == '__main__':
    app.run(debug=True)

flask web 编程学习笔记2:框架


Flask使用jinja2 模版引擎,Jinja2 是一个现代的,设计者友好的,仿照 Django 模板的 Python 模板语言。 它速度快,被广泛使用,并且提供了可选的沙箱模板执行环境保证安全。

'''
    Note2. Frameworks And Example.
'''
#
##########################################################
# 
#   Flask have web frameworks jinja2.Jinja2 include in 
# Flask, It is a model from django.
#
##########################################################
#
from flask import Flask, render_template, request
#
app = Flask(__name__)
# ...
#
#
# index.html like this: <h1>Hello World!</h1>
# 


@app.route('/')
def index():
    return render_template('index.html')
#
# user.html like this: <h1>Hello, {{ name }}!</h1>
#  


@app.route('/user/<name>')
def user(name):
    return render_template('user.html', name=name)
# more example like this:
#    <p>A value from a dictionary: {{ mydict['key'] }}.</p>
#    <p>A value from a list: {{ mylist[3] }}.</p>
#    <p>A value from a list, with a variable index: {{ mylist[myintvar] }}.</p>
#    <p>A value from an object's method: {{ myobj.somemethod() }}.</p>
# AND: 过滤器
#    Hello, {{ name|capitalize }}
#    |---------------------------------------------------------
#    |  过滤器名   |  说  明
#    |---------------------------------------------------------
#    | safe        | 渲染值时不转义
#    | capitalize  | 把值的首字母转换成大写,其他字母转换成小写
#    | lower       | 把值转换成小写形式
#    | upper       | 把值转换成大写形式
#    | title       | 把值中每个单词的首字母都转换成大写
#    | trim        | 把值的首尾空格去掉
#    | striptags   | 渲染之前把值中所有的HTML 标签都删掉
#    |---------------------------------------------------------
#     safe 过滤器值得特别说明一下。默认情况下,出于安全考虑,Jinja2 会转义所有变量。
# 例如,如果一个变量的值为'<h1>Hello</h1>',Jinja2 会将其渲染成'&lt;h1&gt;Hello&lt;/
# h1&gt;',浏览器能显示这个h1 元素,但不会进行解释。很多情况下需要显示变量中存储
# 的HTML 代码,这时就可使用safe 过滤器。
# tell.html like this
# <html>
#   <body>
#       <!-- 变量测试 -->
#       <p>A value from a dictionary capitalize: {{ dict['key']|capitalize}}.</p>
#       <p>A value from a dictionary upper: {{ dict['key']|upper}}.</p>
#       <p>A value from a list: {{ list[1] }}.</p>
#       <p>A value from a dictionary safe: {{ dict['html'|safe] }}.</p>
#       <p>A value from a dictionary no safe: {{ dict['html'] }}.</p>
#       <p>A value from a list, with a variable index: {{ list[intvar] }}.</p>
#       <p>A value from a dictionary: {{ dict['key'] }}.</p>
#       <!-- 判断测试 -->
#       {% if user %}
#           Hello, {{ user }}!
#       {% else %}
#           Hello, Stranger!
#       {% endif %}
#       <!-- 循环测试 -->
#       <ul>
#           {% for comment in comments %}
#               <li>{{ comment }}</li>
#           {% endfor %}
#       </ul>
#       <!-- 宏测试 -->
#       {% macro render_comment(comment) %}
#           <li>{{ comment+'Marco' }}</li>
#       {% endmacro %}
#       <ul>
#           {% for comment in comments %}
#               {{ render_comment(comment) }}
#           {% endfor %}
#       </ul>
#   </body>
# </html>
#
# 为了重复使用宏,我们可以将其保存在单独的文件中,然后在需要使用的模板中导入:
# {% import 'macros.html' as macros %}
# <ul>
# {% for comment in comments %}
#   {{ macros.render_comment(comment) }}
# {% endfor %}
# </ul>
#
# 特别说明:
#     若是需要使用静态文件,图片,css等等,需要新建一个static的文件夹到代码目录,
# 然后采用相对路径的方式调用:"../static/img/xxx.jpg"
#


@app.route('/tell', methods=['GET', 'POST'])
def tell():
    mydict ={'key':"dict,hellworld","html":"<p> mm </p>"}
    mylist = ['list0','list,hellworld']
    myintvar = 0
    mcomments = ["hhhhh","ssssss","ddddddd"]
    #  可以用字典在装载所有变量,然后传递给函数。
    dict_tell = {
                'dict':mydict,
                'list':mylist,
                'intvar':myintvar,
                'comments':mcomments                
                } 

    if request.method == 'POST':
        print("Button POST !")
    '''
    return render_template('tell.html',dict=mydict,\
                                       list=mylist,\
                                       intvar=myintvar,\
                                       comments=mcomments)
     '''
    return render_template('tell.html',**dict_tell)
# 可以使用继承的方式。                                      
#
# base.html
#
# <html>
#	 <head>
#		 {% block head %}
#			 <title>
#				 {% block title %}{% endblock %}- My Application
#			 </title>
#		 {% endblock %}
#	 </head>
#	 <body>
#		 {% block body %}
#		 {% endblock %}
#	 </body>
# </html>
#
# extend.html
#
# {% extends "base.html" %}
# {% block title %}Index{% endblock %}
# {% block head %}
#   {{ super() }}
#   <style>
#   </style>
# {% endblock %}
# {% block body %}
#   <h1>Hello, World!</h1>
# {% endblock %}
#


@app.route('/extend')
def mextend():
    return render_template('extend.html')
#   
#
# app.run will run forever ,It can set some param to contorl run status.  
# 
if __name__ == '__main__':
    app.run(debug=True)

flask web 编程学习笔记1:基础知识


Flask是一个使用 Python 编写的轻量级 Web 应用框架,能迅速的建立一个http服务器,下面代码就是一些简单的例子。
复制代码直接执行,然后就可以在浏览器中输入127.0.0.1浏览自己的网页。

'''
    Note1. Base App And Sample Example.
'''
#
##############################################################
#     You must to install flask framework before you use it.
# Use shell like this: python3 -m pip install flask (in shell)
# or pip install flask (in cmd)
##############################################################
#
from flask import Flask,request,make_response,redirect
# 
app = Flask(__name__)
#
# Route is base of html output way.
# 1. Instrcution route:
#    '/'    -- root, if you browse input :192.168.1.100:5050, 
#              You will see this page.
#    '/xxx' -- some page like 192.168.1.100:5050/xxx.
#    'name' -- use for a variable values.
# 2. app.url_map show all url
# 3. @app.route == app.add_url_rule()
#
@app.route('/')
def index():
    print(app.url_map)
    #
    # Will Show: 
    # Map([<Rule '/User-Agent' (HEAD, GET, OPTIONS) -> userAgent>,
    #      <Rule '/' (HEAD, GET, OPTIONS) -> index>,
    #      ...
    #      <Rule '/static/<filename>' (HEAD, GET, OPTIONS) -> static>,
    #      <Rule '/user/<name>' (HEAD, GET, OPTIONS) -> user>])
    #      
    return '<h1>Hello World!</h1>'
#
# 1. http invalid request: 400 404
#         valid request  : 200
# 2. app.route can return max 3 part. like: return '<h1>Bad Request</h1>', 400 .
#    3rd is part of dict name header.
#    or you can use make_response link it 
#    
@app.route('/make_response')
def mresponse():
    response = make_response('<h1>This document carries a cookie!</h1>')
    response.set_cookie('answer', '42')
    return response
#
# redirect to another html or web page.
#    
@app.route('/unkown')
def mredirect():
    return redirect('http://www.163.com')
#
# 上下文:程序上下文和请求上下文
# 1. 单线程内使用,不影响其他线程。 
# 2. 保存当前线程上下文信息,待到处理函数处理的时候,可以实现异步处理。
# 3. 说明:
# |----------------------------------------------------------------------------------
# |   变量名   |   上下文   |      说  明                                          
# |----------------------------------------------------------------------------------
# |current_app | 程序上下文 | 当前激活程序的程序实例.                                
# |g           | 程序上下文 | 处理请求时用作临时存储的对象,每次请求都会重设这个变量. 
# |request     | 请求上下文 | 请求对象,封装了客户端发出的HTTP请求中的内容.          
# |session     | 请求上下文 | 用户会话,用于存储请求之间需要“记住”的值的词典.        
# |----------------------------------------------------------------------------------
# Example:
#   from hello import app
#   from flask import current_app
#   app_ctx = app.app_context()
#   app_ctx.push()
#   print(current_app.name) 
#   app_ctx.pop()
#
@app.route('/User-Agent')
def userAgent():
    user_agent = request.headers.get('User-Agent')
    return '<p>Your browser is %s</p>' % user_agent  
#    
@app.route('/user/<name>')
def user(name):
    return '<h1>Hello, %s!</h1>' % name
#
# app.run will run forever ,It can set some param to contorl run status.  
# 
if __name__ == '__main__':
    app.run(debug=True)
    
'''
    Extend function. Hook and Flask-Script
'''  
# |===========================
# | 1. Hook -- define 4 hooks
# |===========================
#• before_first_request:注册一个函数,在处理第一个请求之前运行。
#• before_request:注册一个函数,在每次请求之前运行。
#• after_request:注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行。
#• teardown_request:注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行。
#
# |===========================
# | 2. Flask-Script
# |===========================
#
#  1) install: python3 -m pip install flask-script.
#  2) example:
#     from flask.ext.script import Manager
#     manager = Manager(app)
#     #...
#     if __name__ == '__main__':
#         manager.run()
#
#  3)when run app.py will display:usage: hello.py [-h] {shell,runserver} ...
#    DISPLAY:
#    positional arguments:
#       {shell,runserver}
#       shell 在Flask 应用上下文中运行Python shell
#       runserver 运行Flask 开发服务器:app.run()
#    optional arguments:
#       -h, --help 显示帮助信息并退出
#    AND:
#    usage: app.py runserver [-h] [-t HOST] [-p PORT] [--threaded]
#          [--processes PROCESSES] [--passthrough-errors] [-d] [-r]
#   optional arguments:
#       -h, --help 显示帮助信息并退出
#       -t HOST, --host HOST
#       -p PORT, --port PORT
#       --threaded
#       --processes PROCESSES
#       --passthrough-errors
#       -d, --no-debug
#       -r, --no-reload
#       --host 参数是个很有用的选项,它告诉Web 服务器在哪个网络接口上监听来自客户端的
#              连接。默认情况下,Flask 开发Web 服务器监听localhost 上的连接,所以只接
#              受来自服务器所在计算机发起的连接。下述命令让Web 服务器监听公共网络接口
#              上的连接,允许同网中的其他计算机连接服务器:
#
#