《Flask web 开发从入门到精通》读书笔记(下)

《Flask web 开发从入门到精通》读书笔记(下)

第 6 章 Flask中的身份验证机制

6.1 创建简单的基于会话的身份验证

当用户首次登陆,用户的详细信息在应用程序服务器端的会话中被设置,并存储于浏览器的cookie中,随后,当用户打开应用程序时,存储于cookie的细节信息用于检查当前会话,如果会话处于活动状态,则用户将自动登录

SECRET_KEY是一项应用程序配置设置,否则存储在 cookie 中的数据和服务器的 session 的数据将显示位纯文本,这样很不安全。

我们将自己完成一个简单的认证机制。
(1) 创建模型并储存用户详细信息

from werkzeug.security import generate_password_hash, check_password_hash
from flask_wtf import FlaskForm
from wtforms import TextField, PasswordField
from wtforms.validators import InputRequired, EqualTo
from my_app import db


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(100))
    pwdhash = db.Column(db.String())
 
    def __init__(self, username, password):
        self.username = username
        self.pwdhash = generate_password_hash(password)
 
    def check_password(self, password):
        return check_password_hash(self.pwdhash, password)

不建议在数据库中直接存储密码,存储密码的加盐(salted)哈希值
generate_password_hash方法,传入 想加密的字符串即可。需要注意的是,如果要保存到数据库中记得设置String(128),64可能不太够存储
check_password_hash方法用来做密码的加密和对比。第一个参数传入我们需要对比的密码,第二个参数传入我们的明文密码。这样这个函数就帮我们完成了先加密再对比的操作。如果相等,那么将返回True,否则返回False

(2) 创建2张表单用于用户注册和登陆

class RegistrationForm(FlaskForm):
    username = TextField('Username', [InputRequired()])
    password = PasswordField(
        'Password', [
            InputRequired(), EqualTo('confirm', message='Passwords must match')
        ]
    )
    confirm = PasswordField('Confirm Password', [InputRequired()])


class LoginForm(FlaskForm):
    username = TextField('Username', [InputRequired()])
    password = PasswordField('Password', [InputRequired()])

(3)View视图中的处理注册和登陆时的用户请求
基本逻辑如下:GET请求中,请求用户输入用户名和密码,POST请求中验证表单,判断用户名是否重复,否的话在数据库中新增用户并重定向到登陆页面。
退出时候
检查session中是否有用户名,直接删除

@auth.route('/logout')
def logout():
	if 'username' in session:
		session.pop('username')
		flash('You have successfully logged out.','success')
	return redirect(url_for("auth.home"))

6.2 基于Flask-Login扩展的身份验证

$ pip3 install Flask-Login

文档

from flask_login import LoginManager

# Do other application config

login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login' 

Flask-Login 使得实现 Remember me 特性相当简单。仅仅需要性 login_user()方法传递 remember=True。这将在用户电脑上保存一个 cookie,当 session 是存活的时候,Flask-Login 会自动登录。读者可以自行实现。

另请参阅:flask 中的g对象

6.3 使用 Facebook 进行身份验证

许多站点通过第三方身份验证向用户提供了登录选项,使用OAuth2的授权开放标准
OAuth2 仅与SSL协同工作

$ pip install pyopenssl

app.run()方法中添加额外选项

app.run(debug=True,ssl_context='adhoc')

浏览器将显示与不安全的证书相关的警告,可以暂时忽略

提示:
在产品系统中,SSL证书应从权威机构获取
安装Flask-Dance并生成Facebook证书

pip install Flask-Dance

修改部分

app.config["FACEBOOK_OAUTH_CLIENT_ID"] = 'some facebook client ID'
app.config["FACEBOOK_OAUTH_CLIENT_SECRET"] = 'some facebook client secret'
from flask_dance.contrib.facebook import make_facebook_blueprint, facebook

facebook_blueprint = make_facebook_blueprint(scope='email', redirect_to='auth.facebook_login')

6.5 使用Google进行身份验证

6.6 使用Twitter 进行身份验证

6.6 基于LDAP的身份验证机制

单点登陆
使用docker 生成LDAP服务器用于演示
利用docker创建LDAP服务器

$ pip install python-ldap

实现方式

import ldap
app.config['LDAP_PROVIDER_URL'] = 'ldap://localhost'

def get_ldap_connection():
	conn = ldap.initialize(app.config['LDAP_PROVIDER_URL'])
	return 

第 7 章 构建RESTful API

REST调用将API分离成逻辑资源,可以使用HTTP请求来访问和操作,其中每个请求由以下方法组成 GET,POST,PUT,PATCH,DELETE,且每种方法涵盖了各自的含义

第 8 章 Flask 应用程序管理接口

8.1 创建简单的 CRUD 接口

CRUD 的含义是创建,读取,更新和删除

这一节主要是创建简单的管理接口,以使管理用户可在记录上执行其他普通用户无法执行的操作,包括创建,更新喝删除用户记录。

管理用户不应查看喝修改任何用户的密码

8.2 使用Flask-Admin 扩展

文档

$ pip install Flask-Admin

8.3 利用Flask-Admin 注册模型

这一节,太无趣了,略过(主要是看不懂)

8.4 创建自定义表单和操作

通过Flask-Admin 提供的表单创建自定义表单,除此之外还将利用自定义表单创建自定义操作

from flask_admin.form import rules

8.5 使用 WYSIWYG (所见即所得) 编辑器

了解CKEditor编辑器的使用方式,进而构建漂亮的文本框

from wtforms import TextField, PasswordField, BooleanField, widgets, \
    TextAreaField


class CKTextAreaWidget(widgets.TextArea):
    def __call__(self, field, **kwargs):
        kwargs.setdefault('class_', 'ckeditor')
        return super(CKTextAreaWidget, self).__call__(field, **kwargs)


class CKTextAreaField(TextAreaField):
    widget = CKTextAreaWidget()

出来的效果就是有一个文本框

在这里插入图片描述
详细内容还可参考这儿

8.6 创建用户角色

第 9 章 国际化和本地化

web 应用程序中如何添加各国语言,以及语言切换

9.1 添加一种新语言

文档

pip install Flask-Babel

安装它的时候会顺便安装Babel、pytz、speaklater这三个包,其中 Babel 是 Python 的一个国际化工具包。pytz 是处理时区的工具包,speaklater 相当于是 Babel 的一个辅助工具

Flask-Babel 扩展向任何Flask 应用程序中加入了对 internationalization(il8n) 和localization(110n)方面的支持

第10 章 调试,错误处理和测试

10.1 设置基本的文件日志系统

10.2 出现错误时发送电子邮件

先设置

app.config['LOG_FILE'] = 'application.log'

if not app.debug:
    import logging
    logging.basicConfig(level=logging.INFO)
    from logging import FileHandler, Formatter
    from logging.handlers import SMTPHandler
    file_handler = FileHandler(app.config['LOG_FILE'])
    app.logger.addHandler(file_handler)
    mail_handler = SMTPHandler(
        ("smtp.gmail.com", 587), 'sender@gmail.com', RECEPIENTS,
        'Error occurred in your application',
        ('some_email@gmail.com', 'some_gmail_password'), secure=())
    mail_handler.setLevel(logging.ERROR)
    # app.logger.addHandler(mail_handler)
    for handler in [file_handler, mail_handler]:
        handler.setFormatter(Formatter(
            '%(asctime)s %(levelname)s: %(message)s '
            '[in %(pathname)s:%(lineno)d]'
        ))

mail_handler将日志级别设置为ERROR,电子邮件仅在出现严重错误时才有出现的必要

确保关闭run.py文件中的debug标记,以启用应用程序日志功能

使用

@catalog.route('/')
@catalog.route('/<lang>/')
@catalog.route('/<lang>/home')
@template_or_json('home.html')
def home():
    products = Product.query.all()
    app.logger.info(
        'Home page with total of %d products' % len(products)
    )
    return {'count': len(products)}

当页面不存在时(错误404),也可记录相关错误

@app.errorhandler(404)
def page_not_found(e):
	app.logger.error(e)
	return render_template('404.html'),404

10.3 使用 Sentry监视异常

Sentry 简化了异常监视过程,同时针对应用程序用户所面临的错误提供了某种洞察能力。日志文件中很可能存在我们无法发现的错误。Sentry 将错误划分到不同的类别中,并记录错误的重复次数。Sentry 提供了一个较好的GUI。

Sentry 是一项云服务,可以注册使用

$ pip install sentry-sdk[flask] 

配置

import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration

sentry_sdk.init(
    dsn="https://1234:5678@fake-sentry-server/1",
    integrations=[FlaskIntegration()]
)

10.4 基于pdb的调试机制

略过

10.5 创建简单的测试

import os
from my_app import app, db
import unittest
from unittest import mock
import tempfile
class CatalogTestCase(unittest.TestCase):

    def setUp(self):
        self.test_db_file = tempfile.mkstemp()[1]
        app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + self.test_db_file
        app.config['TESTING'] = True
        self.app = app.test_client()
        db.create_all()

tempfile.mkstemp 是创建临时文件,返回的是两个参数,一个是fd,一个是fname,fd是文件描述符,fname是指生成的文件的绝对路径

上述方法在各个测试之前运行
测试完毕之后,移除临时文件

def tearDown(self):
    os.remove(self.test_db_file)

上述方法在每项测试运行之后调用,以确保每次测试使用新的数据库文件
最后编写测试用例

def test_home(self):
    rv = self.app.get('/')
    self.assertEqual(rv.status_code, 200)

开始测试

if __name__ == '__main__':
	unittest.main()

返回200,代表测试成功

10.6 针对视图和逻辑编写多项测试

针对每个功能编写单独的测试用例

10.7 nose库集成

nose库可简化测试过程,自动发现测试用例
需要先编写好测试用例

$ pip install nose 

然后nosetests -v 开始测试

$ nosetests -v
Test creation of new category ... ok
Test creation of new product ... ok
Test home page ... ok
Test Products list page ... ok
Test searching product ... ok
---------------------------------------------------------------
Ran 5 tests in 0.399s

OK 

为了运行单个测试文件,需使用下面命令:

$ nosetests app_tests.py 

现在,如果需要运行单个测试,可以使用下面命令:

$ nosetests app_tests:CatalogTestCase.test_home 

10.8 使用 Mocking 避免访问API

在某些时候,在测试期间进行调用不仅昂贵,而且还会影响该服务的统计数据。对此,Mocking 饰演了重要的角色

单元测试mock模块介绍

mock 是unittest 库中的标准包
需要安装 geoip2 库,工作中难免会遇到处理ip相关的需求,比如根据ip的请求的地理位置,访问频率来调整业务。这里介绍一个处理ip相关的模块geoip2

$ pip install geoip2

安装后,我们需要下载一个ip库,用来匹配解析与ip相关的参数。点击下载,这里我们选择粒度细分到城市的下载链接,这里仅介绍mmdb格式的使用方式。(现在需要注册才能下载)GeoLite2-City.tar.gz

设想要存储创建产品的用户位置(用户会从全球各地创建产品)

import geoip2.database
from geoip2.errors import AddressNotFoundError
reader = geoip2.database.Reader('GeoLite2-City_20190416/GeoLite2-City.mmdb')
try:
    match = reader.city(request.remote_addr)
except AddressNotFoundError:
    match = None
product = Product(
    name, price, category, filename,
    match and match.location.time_zone or 'Localhost')

10.9 确定测试覆盖率

$ pip install coverage

最简单的获取覆盖率细节的方法是使用命令行。仅需运行下面命令:

$ coverage run –source=../<Folder name of application> --omit=app_tests.py,run.py app_tests.py 

这里–source 表示需要覆盖率中需要考虑的目录,–omit 表示需要忽略的文件。

现在,在终端打印报告,需运行:

$ coverage report 

在这里插入图片描述
为了得到覆盖率 HTML 形式的输出,运行下面命令:

$ coverage html 

或者,我们可以在测试文件中包含一段代码,这样每次运行测试时都会获得覆盖率报告。在 app_tests.py 中最前面添加以下代码片段:

import coverage
cov = coverage.coverage(
    omit = [
    '/Users/shalabhaggarwal/workspace/mydev/lib/python2.7/sitepackages/*',
    'app_tests.py'
    ]
)
cov.start() 

告诉 coverage 忽略所有的 site-packages(通常 coverage 会包含所有的依赖),以及测试文件本身。

if __name__ == '__main__':
    try:
        unittest.main()
    finally:
        cov.stop()
        cov.save()
        cov.report()
        cov.html_report(directory = 'coverage')
        cov.erase() 

现在运行命令:

$ python app test.py 

10.10 使用性能分析查找瓶颈

generate_profile.py中编写

from werkzeug.contrib.profiler import ProfilerMiddleware
from my_app import app

app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions = [10])
app.run(debug=True)

运行应用:

$ python generate_profile.py 

在这里插入图片描述

第 11 章 部署和后期部署

11.1 基于Apache 的部署机制

对于 Python web 应用来说,我将使用 mod_wsgi,它实现了一个简单的 Apache 模块,可以托管实现WSGI 接口的任何 Python 应用

mod_wsgi 不同于 Apache,需要被单独安装

11.1.1 准备工作

安装应用程序
在setup.py中

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import os
from setuptools import setup

setup(
    name = 'my_app',
    version='1.0',
    license='GNU General Public License v3',
    author='Shalabh Aggarwal',
    author_email='contact@shalabhaggarwal.com',
    description='Catalog application for Flask',
    packages=[
        'my_app',
        'my_app.catalog',
    ],
    platforms='any',
    install_requires=[
        'Flask>=0.10.1',
        'flask-sqlalchemy',
        'flask-wtf',
        'flask-babel',
        'sentry-sdk',
        'blinker',
        'geoip2',
    ],
    include_package_data=True,
    classifiers=[
        'Development Status :: 4 - Beta',
        'Environment :: Web Environment',
        'Intended Audience :: Developers',
        'License :: OSI Approved :: GNU General Public License v3', 
        'Operating System :: OS Independent',
        'Programming Language :: Python',
        'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
        'Topic :: Software Development :: Libraries :: Python Modules'
    ],
    zip_safe = False,
)

include_package_data 从相同目录下的 MANIFEST.in 文件里读取内容
classifiers 程序的所属分类列表
zip_safe 标记告诉安装者不要将这个应用作为 ZIP 文件进行安装
MANIFEST.in 文件的内容

recursive-include my_app/templates *
recursive-include my_app/static *
recursive-include my_app/translations *

默认情况下,当您实际上打包python代码(使用python setup.py sdist)以创建压缩存档以进行分发时,打包程序将仅在软件包存档中包含一组特定文件(python代码本身)。如果存储库中包含文本文件(例如,模板,图形,文档),该怎么办?默认情况下,打包程序不会在归档文件中包含这些文件,并且它是不完整的。MANIFEST.in允许您覆盖默认值,确切指定需要在zip存档中包含哪些文件才能分发。

利用下列命令安装应用程序

$ python setup.py install 

mod_wsgi 的安装跟操作系统有关系。

在ubuntu上

sudo apt-get update && sudo apt-get install apache2 apache2-utils ssl-cert libapache2-mod-wsgi -y

11.1.2 实现方式

(1)创建app.wsgi

activate_this = '/home/ubuntu/workspace/cookbook11/bin/activate_this.py'
exec(open(activate_this).read(), dict(__file__=activate_this))

from my_app import app as application
import sys, logging
logging.basicConfig(stream = sys.stderr)

前两行用于激活环境
最后两行为可选项。只是将结果流通过 标准输出

app 对象需要作为 application 进行导入,因为 mod_wsgi 期望 application 这样的关键字

(2)配置文件apache_wsgi.conf,Apache HTTP 服务器将根据配置文件为应用程序提供服务

<VirtualHost *>

    WSGIScriptAlias / /home/ubuntu/workspace/cookbook11/Chapter-11/app.wsgi

    <Directory /home/ubuntu/workspace/cookbook11/Chapter-11>
        Require all granted
        Allow from all
    </Directory>

    Alias /static/uploads/ "/home/ubuntu/workspace/cookbook11/Chapter-11/flask_test_uploads/"
    <Directory "/home/ubuntu/workspace/cookbook11/Chapter-11/flask_test_uploads">
        Require all granted
        Options Indexes
        Allow from all
        IndexOptions FancyIndexing
    </Directory>

</VirtualHost>

(3)向/etc/apache2/apache2.conf 中添加apache_wsgi.conf文件

Include <Path to application>/flask_catalog_deployment/apache_wsgi.conf 

11.1.3 工作方式

sudo systemctl restart apache2.service

期间的错误信息可在/var/log/apache2/error_log中被看到

11.2 基于 uWSGI 和 Nginx 的部署机制

需要禁用其他正在运行的服务器 如Apache 服务器
根据操作系统安装

$ sudo apt-get install nginx
$ pip install uWSGI

另外,确保针对Nginx设置site-enabled文件夹
创建uwsgi.ini 的文件

[uwsgi]
http-socket = :9090
plugin = python
wsgi-file = <Path to application>/flask_catalog_deployment/app.wsgi
processes = 3 

启动
为了测试 uWSGI 是否正常工作,需运行下面命令:

$ uwsgi --ini uwsgi.ini 

前面命令等同于运行下面命令:

$ uwsgi --http-socket :9090 --plugin python --wsgi-file app.wsgi 

在进行后续操作之前,编辑上述文件并利用socket 替换http-socket,这将把协议从HTTP 修改为 uWSGI

创建一个nginx-wsgi.conf的新文件,该文件包含了Nginx 配置内容,并对应用程序和静态内容提供服务

server {
  location / {
    include uwsgi_params;
    uwsgi_pass 127.0.0.1:9090;
  }
  location /static/uploads/ {
    alias /home/ubuntu/workspace/cookbook11/Chapter-11/flask_test_uploads/;
  }
}

创建一个软连接到 sites-enabled 文件夹:

$ ln -s <path/to/nginx-wsgi.conf> <path/to/sites-enabled> 

编辑 nginx.conf 文件(通常位置是/etc/nginx/nginx.conf),在最后一个“}”前的第一个服务器代码块内增加下面行:

include <path/to/sites-enabled>/*; 

重启 Nginx:

$ sudo nginx -s reload 

浏览器输入http://127.0.0.1/来看通过 Nginx 和 uWSGI 服务的程序。

参考文章1

11.3 基于 Gunicorn 和 Supervisor 的部署机制

Gunicorn 是一个针对 Unix 的 WSGI HTTP 的服务器。它非常的轻便,快速。它的简单性在于它与各种 web 框架的广泛兼容性。

Supervisor 是一个监控工具,用于控制各种子处理过程,并且在这些进程异常退出的时候重启它们。它能够被扩展使用 XML-RPC API 控制远程位置上的程序,而不用登录远程服务器(我们不会在这里讨论这,因为已经超出了本书的范围)。 需要知道的是这些工具能够和之前章节提到的工具比如 Nginx 一起配合。

$ pip install gunicorn
$ pip install supervisor 

检查 gunicorn 是否正常工作,需在应用文件夹里运行下面命令:

$ gunicorn -w 4 -b 127.0.0.1:8000 my_app:app 

virtualenv环境中运行一下命令

$ echo_supervisord_conf > etc/supervisord.conf 

Supervisor 通常会寻找 etc 文件夹,里面存在一个 supervisord.conf 文件。在系统层面的安装下,这个文件夹是/etc,但在 virtualenv 里,会在 virtualenv 里寻找 etc,然后返回到/etc/,因此建议在virtualenv 中创建名为etc的文件夹。
echo_supervisord_conf 是由 Supervisor 提供的,它向特定位置输出一个配置文件。

上述命令将会在 etc 文件夹里创建一个叫做 supervisord.conf 的文件。在这个文件里添加下面代码块:

[program:flask_catalog]
command=<path/to/virtualenv>/bin/gunicorn -w 4 -b 127.0.0.1:8000 my_
app:app
directory=<path/to/virtualenv>/flask_catalog_deployment
user=someuser # Relevant user
autostart=true
autorestart=true
stdout_logfile=/tmp/app.log
stderr_logfile=/tmp/error.log 

配置完毕后

$ supervisord
$ supervisorctl status
flask_catalog RUNNING pid 40466, uptime 0:00:03 

第一个命令启动 supervisord 服务器,接下来查看所有进程的状态

每次当修改应用的时候,都需要重启 Gunicorn,以便让这些修改生效,运行下面命令:

$ supervisorctl restart all 

你可以重启特定程序而不是所有:

$ supervisorctl restart flask_catalog 

11.4 基于 Tornado 的部署

11.5 针对文件上传使用 S3 存储

11.6 基于 HeroKu 的部署

11.7 基于 AWS Elastic Beanstalk 的部署机制

11.8 利用 New Relic 管理和监控应用程序

上面几节内容,工作中不太用的到,过

第 12 章 微服务和容器

12.1 基于Docker 的容器化机制

这一节的内容,之前学习docker 的时候了解过

12.2 基于 Kubernetes 的编排容器

这一节涉及到Kubernetes 咱没有学过,本书介绍的特别简单,后续深入学习再做笔记

12.3 AWS Lambda 上基于Zappa的无服务器操作

第 13 章 提示和技巧

通常,WSGI 应用是同步和阻塞的,不能同时处理多个同步请求。我们将看到如何通过一个简单的例子解决这个问题

13.1 实现基于 Whoosh 的全文本搜索

whoosh,纯python的全文搜索引擎
具体介绍参考这儿

查了一下,知乎上说这个太慢了,没人使用,而且GitHub上的时间都是5-7年前的,太久远了

13.2 实现基于 Elasticsearch 的全文本搜索

需要安装python包和Elasticsearch 服务器自身

pip install elasticsearch

从这儿下载。解压文件,然后运行下面命令:

$ bin/elasticsearch 

。。。。

13.3 与信号协同工作

我们将使用一个叫做 binker 的 Python 库,它提供了一些信号特性。Flask 内建了对 blinker 的支持,可以很大程度上使用信号。其中一些核心信号是由 Flask 提供的

参考资料 关于blinker库
参考资料 关于 Flask 支持的核心信号列列表
Flask-SQLAlchemy 提供的信号可以在找到

13.4 在应用程序中使用缓存

$ pip install Flask-Cache 

首先,需初始化 Cache。将在应用配置里进行处理,my_app/__init__.py

from flask.ext.cache import Cache

cache = Cache(app, config={'CACHE_TYPE': 'simple'}) 

这里,使用 simple 做为 Cache 的类型,缓存存储在内存里。不建议在生产环境这样做。对于生产环境,需使用 RedisMemcached,文件系统等等这些。Flask-Cache 对这些全部支持。

接下来,需在方法里增加缓存;这是非常容易实现的。我们仅仅需要对视图方法增加@cache.cached(timeout=)装饰器。列出所有的商品类别可以这样做(在 views.py 中处理):

from my_app import cache
@catalog.route('/categories')
@cache.cached(timeout=120)
def categories():
    # Fetch and display the list of categories 

这种缓存方式以键值对的形式进行存储,键为请求路径,值为这个方法的输出值。

Flask-Caching 文档

13.5 实现Flask 应用程序 的电子邮件支持

$ pip install Flask-Mail 

13.6 了解异步操作

电子邮件的发送可以在后台完成

from threading import Thread

def send_mail(message):
    with app.app_context():
        mail.send(message)

t = Thread(target=send_mail, args=[message])
t.start()

13.7 与 Celery 协同工作

Celery 是为 Python 准备的任务队列。早期的时候有一个扩展是集成了 Flask 和 Celery,但是在 Celery3.0 的时候,这个扩展被废弃了。现在,Celery 可以直接在 Flask 里使用,而仅仅需做一些配置。前面小节,我们实现了异步发送邮件,这一小节将使用 Celery 完成同样的功能。

 pip install celery 
from celery import Celery

app.config.update(
    CELERY_BROKER_URL='redis://localhost:6379',
    CELERY_RESULT_BACKEND='redis://localhost:6379'
)
def make_celery(app):
    celery = Celery(
        app.import_name, broker=app.config['CELERY_BROKER_URL']
    )
    celery.conf.update(app.config)
    TaskBase = celery.Task
    class ContextTask(TaskBase):
        abstract = True
        def __call__(self, *args, **kwargs):
            with app.app_context():
                return TaskBase.__call__(self, *args, **kwargs)
    celery.Task = ContextTask
    return celery 

大部分情况下可以在应用里这样使用:

celery = make_celery(app) 

运行 Celery 进程,需执行下面命令:

$ celery worker -b redis://localhost:6379 --app=my_app.celery -l INFO 

-b 指定了 broker,-app 指定了在配置文件里创建的 celery 对象。 现在,仅仅需要在 views.py 里使用 celery 对象去异步发送邮件
使用

from my_app import celery

@celery.task()
def send_mail(message):
    with app.app_context():
        mail.send(message)

# Add this line wherever the email needs to be sent
send_mail.apply_async((message,)) 

Celery - Distributed Task Queue 文档

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Flask Web开发从入门到精通》是一本介绍如何使用Flask来进行Web开发的书籍。Flask是一个基于Python的轻量级Web框架,它简单易学、灵活且功能强大,适用于各种规模的Web开发项目。 这本书从基础概念开始讲解,详细介绍了Flask的工作原理、核心组件和基本用法。读者可以学习如何搭建一个简单的Flask应用,并通过实例了解路由、模板、表单处理、数据库操作等关键知识点。书中还提供了大量的示例代码和实践项目,帮助读者深入理解Flask的各种功能和技术应用。 同时,《Flask Web开发从入门到精通》也从入门到精通的过程,逐步介绍了如何构建复杂的Web应用。读者可以学习如何优化性能、处理用户认证和授权、实现RESTful API等高级技术。此外,书中还对与其他常用工具如数据库、前端框架的集成等进行了探讨。 云盘是指云存储服务,它通过网络为用户提供存储和备份服务。在网络时代,云盘成为了人们共享和传输文件的重要方式。对于Flask Web开发从入门到精通这样的书籍来说,如果有相关的云盘提供,会方便读者获取书籍的各种资源,如代码示例、实例项目、扩展模块等。 利用云盘存储这些资源,读者可以方便地进行下载、备份和共享。此外,云盘还提供了多种访问方式,可以在不同的设备上随时随地进行访问,方便读者在学习过程中的查阅和使用。 总而言之,《Flask Web开发从入门到精通》是一本全面介绍Flask框架的书籍,通过学习这本书,读者可以掌握Flask的基础知识和高级应用技巧。如果提供相应的云盘服务,将对读者的学习和使用带来更多的便利。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jaffe—fly

古人学问无遗力,少壮工夫老始成

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值