Flask项目配置

创建项目

flask 项目不能像 django 那样通过命令行创建,所以直接在 pycharm 中新建 Flask 项目。

假如我们创建了一个名为 Demo 的项目,那么创建好的项目目录如下所示:

1
2
3
4
Demo
- static
- templates
- app.py

只有两个文件夹和一个 py 文件,真不愧是轻量级框架。

  • static:存放静态资源,如 css,js
  • templates:存放模板,类似 django
  • app.py:项目启动文件

然后我们就可以启动项目了。在启动项目之前,需要设置 Flask 的环境变量,打开终端,执行以下指令:

1
$env:FLASK_APP=app.py

app.py 就是项目的入口文件,名字依个人而异。

然后启动项目:

1
flask run

项目配置

创建“app”

类似于 Django,我们需要创建一个“app”用来存放各个模块的视图函数、路由配置、模型文件等。

在根目录下创建包 “webapp”,然后在 webapp 下创建所需的模块,再在每个模块下创建 views.py 存放视图函数,创建 models.py 存放数据库模型。

目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Demo
- static
- templates
- webapp
- __init__.py
- user
- __init__.py
- views.py
- models.py
- order
- __init__.py
- views.py
- models.py
...
- app.py

webapp 下的 init.py 文件主要用于创建 flask 应用并加载配置,如 MySQL、redis等。

user 和 order 下的 init.py 文件用于创建蓝图对象,然后在 views.py 文件中导入对象并使用。

user/views.py

1
2
3
4
5
from . import userApp

@userApp.route('/user')
def login():
return 'silence'

user/init.py

1
2
3
4
5
from flask import Blueprint

userApp = Blueprint('user', __name__)

from .view import * # 写在末尾,不然会因循环导入报错

创建配置文件

在根目录新建 settings.py 文件,我们可以在其中自定义配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Config:
# 密钥,用于 session 加密
SECRET_KEY = 'Sm9obiBTY2hyb20ga2lja3MgYXNz'
# 数据库连接格式
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://user:password@localhost:3306/databasename?charset=utf8"


# 发展环境配置
class DevelopmentConfig(Config):
DEBUG = True


# 生产环境配置
class ProductionConfig(Config):
DEBUG = False


config = {
'development': DevelopmentConfig,
'production': ProductionConfig
}

配置项是需要加载到项目中的,可以在 app.py 中加载,也可以在 webapp/init.py 中加载。

项目默认在 app.py 中创建 Flask 实例,我们也可以将这部分代码封装到一个函数中,还可以添加更多的其它功能。

在 webapp/init.py 中添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from flask import Flask
from settings import config


def createApp():
# 创建 app
app = Flask(__name__)

# 选择配置类
Config = config['development']

# 加载配置
app.config.from_object(Config)

# 注册蓝图
# 在使用前 import,防止循环导入
from .cart.view import cartApp
app.register_blueprint(userApp)

from .user.view import userApp
app.register_blueprint(cartApp)

return app

然后将注册好的 app 导入 app.py 文件中:

1
2
3
4
5
6
from webapp import createApp

app = createApp()

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

配置数据库

Flask-SQLAlchemy 是一款适用于 Flask 的数据库插件,它支持包含 MySQL、PostgreSQL 和 SQLite 在内的很多数据库软件。

我们可以在开发的时候使用简单易用且无需另起服务的 SQLite(直接以 db 文件保存在项目中),需要部署应用到生产服务器上时,则选用更健壮的 MySQL 或 PostgreSQL 服务。

1. 安装

flask-sqlalchemy 用于连接数据库:

1
pip install flask-sqlalchemy

flask-migrate 用于迁移数据库:

1
pip install flask-migrate
2. 配置 settings.py

在 settings.py 中添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import os

basedir = os.path.abspath(os.path.dirname(__file__))

class Config:
# 密钥,用于 session 加密
SECRET_KEY = 'Sm9obiBTY2hyb20ga2lja3MgYXNz'

# 发展环境配置
class DevelopmentConfig(Config):
DEBUG = True
# 数据库连接格式
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///' + os.path.join(basedir, 'app.db')
# 数据发生变更是否发信号给应用
SQLALCHEMY_TRACK_MODIFICATIONS = False

# 生产环境配置
class ProductionConfig(Config):
DEBUG = False
# 数据库连接格式
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://user:password@localhost:3306/databasename?charset=utf8"

os.environ.get 可以获取本机的用户环境变量,一些比较机密的数据可以放在环境变量里面,仅对自己可见。or 的作用是如果没有定义该环境变量,则选择后面的值。

当然,发展环境也可以用 mysql 数据库。

3. 配置 app/init.py

在 webapp/init.py 文件中添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

# 创建数据库连接对象
db = SQLAlchemy()
# 创建数据库迁移对象
migrate = Migrate()

def createApp():
...
# 初始化数据库连接对象
db.init_app(app)
# 初始化数据库迁移对象
migrate.init_app(app, db)
...
4. 创建数据库模型

在 user/models.py 中添加如下代码:

1
2
3
4
5
6
7
8
9
from webapp import db

class User(db.Model):
__tablename__ = "user" # 设置表名

id = db.Column(db.Integer, autoincrement=True, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(20), nullable=False)

在 cart/models.py 中添加如下代码:

1
2
3
4
5
6
7
8
9
10
from webapp import db

class Cart(db.Model):
__tablename__ = "cart" # 设置表名

id = db.Column(db.Integer, autoincrement=True, primary_key=True)
user = db.Column(db.Integer, db.ForeignKey(‘user.id’)) # 外键 (‘表名.id’)
shopname = db.Column(db.String(200), nullable=False)
price = db.Column(db.Float(2), nullable=False)
date = db.Column(db.DateTime, nullable=False)
生成数据库

先初始化迁移文件:

1
flask db init

会发现根目录下多了一个 migrations 文件夹,就是生成的迁移文件。

然后要创建数据表,有两种方法:

  1. 在项目中的某个文件导入模型类或实例化模型类之后,在终端执行

    1
    flask db migrateflask db  upgrade
  2. 不需要事先在项目中导入模型类,打开终端,进入 shell:flask shell,然后执行

    1
    2
    3
    4
    5
    >>> from webapp import db
    >>> from webapp.user.models import User
    >>> from webapp.cart.models import Cart
    >>> db.create_all()
    >>> quit()

    此方法需要在 shell 中导入要创建的模型类。

之后如果更改了数据库,用以上两种方法即可。

使用数据库

在 user/view.py 中添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from .models import *

@userApp.route('/login', methods=['post'])
def login():
data = request.get_json()
username = data['username']
password = data['password']
email = data['email']

user = User(username=username, password=password, email=email)
db.session.add(user) # 添加数据
db.session.commit() # 提交更改

return jsonify({'username': username, 'password': password})

关于更多 Falsk-SQLAlchemy 对数据库的操作,请看 Falsk-SQLAlchemy


跨域

安装 flask-cors:

1
pip install flask-cors

在入口文件 app.py 中添加如下代码:

1
2
3
from flask_cors import CORS

CORS(app, ssupports_credentials=True)

我们还可以规定 origins(请求源)、methods(请求方法)、allow_headers(允许的请求头)supports_credentials(是否支持 cookie)。


配置 redis
安装 redis 支持包
1
pip install flask-redis
配置 settings.py

在 settings.py 文件中添加如下代码:

1
REDIS_URL = "redis://:password@localhost:6379/0"
配置 webapp/init.py
1
2
3
4
5
6
7
8
9
10
from flask_redis import FlaskRedis

# 创建 redis 对象
redis_client = FlaskRedis()

def createApp():
...
# 初始化 redis 对象
redis_client.init_app(app)
...

接下来就可以通过 redis-client 操作 redis 数据库了。


flask 用 redis 缓存 session
安装 flask-session
1
pip install flask-session
配置 settings.py

添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 配置 session
# 存储方式
SESSION_TYPE = 'redis'
# session 是否长期有效,如为 False 则关闭浏览器 session 失效
SESSION_PERMANENT = True
# session 如果设定为长期有效则设定 session 生命周期,单位是秒(默认为31天)
PERMANENT_SESSION_LIFETIME = 60 * 60 * 24
# 密钥,用于 session 加密
SECRET_KEY = 'Sm9obiBTY2hyb20ga2lja3MgYXNz'
# 是否对发送到浏览器上 session 的 cookie 值进行加密
SESSION_USE_SIGNER = True
# 保存 session 对象的键的前缀
SESSION_KEY_PREFIX = 'session:'
# 配置redis服务器参数,默认为 0 数据库
SESSION_REDIS = StrictRedis(host='localhost', port=6379, db=1, password='123456')
配置 webapp/init.py
1
2
3
4
5
6
7
8
9
10
from flask_session import Session

# 创建 session 对象
session = Session()

def createApp():
...
# 初始化 redis 对象
session.init_app(app)
...
使用 session

我们虽然使用到了 flask-session,但它仅用来配置 session,使用 session 时还是要用 flask 模块中 session:

user/view.py

1
2
3
4
5
6
7
8
9
10
from flask import session

@userApp.route('/register', methods=['post', 'get'])
def login():
# 存
session[key] = value
# 取
val = session.get[key]
if val is None:
return 'None'

用 redis 做 cache 缓存

当你的应用变慢的时候,可以考虑加入缓存。至少这是最简单的加速方法。缓存有什 么用?假设有一个函数耗时较长,但是这个函数在五分钟前返回的结果还是正确的, 那么我们就可以考虑把这个函数的结果在缓存中存放一段时间,当再次请求数据的时候直接从缓存中读取。

安装 flask-cache
1
pip install flask-cache
配置 settings.py
1
2
3
4
5
6
CACHE_TYPE = 'redis'
CACHE_REDIS_HOST = 'localhost'
CACHE_REDIS_PORT = 6379
CACHE_REDIS_DB = '2'
CACHE_REDIS_PASSWORD = '123456'
CACHE_DEFAULT_TIMEOUT = 60 # 过期时间
配置 webapp/init.py
1
2
3
4
5
6
7
8
9
10
from flask_cache import Cache

# 创建 cache 对象
cache = Cache()

def createApp():
...
# 初始化 cache 对象
cache.init_app(app)
...
使用 cache
1
2
3
4
5
6
from webapp import cache

cache.set(key, value) # 添加
cache.get(key) # 获取
cache.delete(key) # 删除
cache.clear() # 清除所有缓存

在 cart/view.py 文件中添加如下代码:

1
2
3
4
5
6
from webapp import cache

@cartApp.route('/cart', methods=['get'])
@cache.cached(timeout=60, key_prefix='cart_index')
def getCartInfo():
...

使用装饰函数 @cache.cached 修饰视图函数,表示将视图函数的返回结果缓存到 redis 中。并且 @cache.cached 必须在 @cartApp.route 之后

@cache.cached 有两个参数,第一个表示过期时间,单位为秒,第二个表示保存在 redis 中的键的前缀。这个前缀名在删除缓存的时候可以用到。

这种方法适用于对所有用户都相同的页面数据进行缓存。

如果要针对每个用户单独进行缓存,可将用户的信息作为 cache 键的一部分,手动进行缓存:

1
2
3
4
5
6
7
8
9
10
11
12
13
from webapp import cache

@cartApp.route('/cart', methods=['get'])
def getCartInfo():
username = session.get('username')
if cache.get('cart_' + username) is not None:
data = cache.get('cart_' + username)
else:
data = []
...
key = 'cart_' + username
cache.set(key, data)
...

当一个视图函数的返回结果发生改变时,缓存也应该进行更新。更新的方法就是在结果发生改变的地方将缓存删除,那么当用户再次请求的时候就会生成新的缓存。


flask 邮件
安装 flask-mail
1
pip install flask-mail
配置 settings.py
1
2
3
4
5
6
7
# 配置邮件(网易邮箱)    
MAIL_SERVER = 'smtp.163.com'
MAIL_PORT = 465
MAIL_USE_SSL = True # 安全协议
MAIL_USERNAME = 'silence@163.com' # 发信服务器的用户名
MAIL_PASSWORD = '' # 授权码
MAIL_DEFAULT_SENDER = ('Silence', 'silence@163.com') # 默认发送人(姓名,邮箱地址)
配置 webapp/init.py
1
2
3
4
5
6
7
8
9
10
from flask_mail import Mail

# 创建 mail 对象
mail = Mail()

def createApp():
...
# 初始化 mail 对象
mail.init_app(app)
...
发送邮件

在 user/view.py 中添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from flask_mail import Message
from webapp import mail

@userApp.route('/register', methods=['post', 'get'])
def register():
...
# 邮件主题
subject = '账号激活'
# 接收者
recipients = [email]
# 邮件内容(html)
html = '点击链接激活邮箱: <a href="http://127.0.0.1:5000/check/%d">xxxxxxxxx</a>' %(user_id)
# 创建邮件内容
message = Message(subject=subject, recipients=recipients, html=html)
# 发送邮件
mail.send(message)
...

# 用户点击链接访问此 url
@userApp.route('/check/<id>')
def chackid(id):
return str(id)

celery 任务队列

celery 是异步任务队列,可以将一些比较耗时的操作(发送邮件)交给它来异步处理,这样用户不需要一直等待,以提高用户体验。

安装 celery
1
pip install celery
配置 settings.py
1
2
3
4
5
6
7
# 配置 celery
# 消息中间件的保存位置
CELERY_BROKER_URL = 'redis://:123456@192.168.239.130:6379/3'
# 返回结果的保存位置
CELERY_RESULT_BACKEND = 'redis://:123456@192.168.239.130:6379/3'
# 任务过期时间
CELERY_TASK_RESULT_EXPIRES = 120
在 webapp/init.py 中创建 celery 对象
1
2
3
4
5
6
7
8
9
10
11
from celery import Celery
from settings import Config

# app 名
celery = Celery(__name__, broker=Config.CELERY_BROKER_URL, backend=Config.CELERY_RESULT_BACKEND)

def createApp():
...
# 完善 celery 配置
celery.conf.update(app.config)
...
创建任务

新建 tasks.py 文件,我们用 celery 来发送邮件,在其中添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from webapp import celery, mail, createApp
from flask_mail import Message

@celery.task()
def send_email(user_id, email):
app = createApp()
mail.init_app(app)
# 创建应用上下文
with app.app_context():
# 邮件主题
subject = '账号激活'
# 接收者
recipients = [email]
# 邮件内容(html)
html = '点击链接激活邮箱: <a href="http://127.0.0.1:5000/check/%d">xxxxxxxxx</a>' % user_id
message = Message(subject=subject, recipients=recipients, html=html)
mail.send(message)

通过 @celery.task() 装饰器来声明这是一个 celery 任务。

关于 flask 应用上下文的介绍,请看 Flask 应用上下文

使用任务

在 user/view.py 文件中添加如下内容:

1
2
3
4
5
6
7
from tasks import send_email

@userApp.route('/register', methods=['post', 'get'])
def register():
...
send_email.delay(user.id, email)
...

celery 开启任务的方法:

1
delay(*args, **kwargs)

直接发送一个任务消息,其传入的参数就是任务函数的参数。

1
apply_async(countdown=60, expires=120)

从现在起一分钟执行,但在两分钟后过期。

开启 celery 服务

Windows:

1
pip install eventletcelery -A tasks worker -l info -P eventlet

Linux:

1
celery -A tasks worker -l info