文章目录
前言
在上一节中,我学习了一个简单但是完整的Flask应用,但是此应用有一个最大的问题,那就是所有的代码都在一个文件中,这样很不便于维护。因此今天将介绍一下Flask常用的多文件组织结构。
一、项目结构
Flask应用的基本结构:
| - flasky
| - app/
| - templates/
| - static/
| - main/
| - _init_.py
| - errors.py
| - forms.py
| - views.py
| - _init_.py
| - email.py
| - models.py
| - migrations/
| - tests/
| - _init_.py
| - test*.py
| - venv/
| - requirements.txt
| - config.py
| - flasky.py
其中有4个顶级文件夹:
- app文件夹用来存储Flask主应用
- migration文件夹用来存储数据库迁移脚本
- test文件夹存储单元测试文件
- venv文件夹存储虚拟环境
除了test文件夹存储的单元测试没有接触过之外,还有三个文件也是上一节中没有涉及的:
- config.py: 统一管理配置文件
- requirements.txt: 列出当前应用所有依赖的包
- flasky.py: 定义Flask应用实例
接下来介绍一下如何将上一节中冗长的hello.py
文件转换成这种结构!
二、项目分解
1. config.py
先将所有的配置单独提取出来,方便后期进行增加或修改:
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config():
SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'
MAIL_SERVER = os.environ.get('MAIL_SERVER', 'smtp.163.com')
MAIL_PORT = os.environ.get('MAIL_PORT', '25')
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
FLASKY_MAIL_SENDER = os.envrion.get('FLASKY_ADMIN')
FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
SQLALCHEMY_TRACK_MODIFICATIONS = False
@staticmethod
def init_app(app):
pass
class DevelopmentConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or \
'sqlite://'
class ProductionConfig(Config):
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
基类实现通用配置,三个子类实现专用配置。config字典注册了不同的配置环境,以及一个默认配置。
2. app/
2.1 init.py
其中app文件夹中,templates/, static/, email.py和models.py和上一节一样,将hello.py中相应的代码复制过去就可以了。
_init_.py中使用工厂函数来创建应用实例。工厂函数的好处是可以创建多个不同配置的应用实例,且配置是可以动态修改的。而以往的写法中,由于app一开始就创建好了,就算后期修改配置也不起作用了。工厂函数使用create_app
定义:
from flask import Flask, render_template
from flask_bootstrap import Bootstrap
from flask_mail import Mail
from flask_moment import Moment
from flask_sqlalchemy import SQLAlchemy
from config import config
bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
bootstrap.init_app(app)
mail.init_app(app)
moment.init_app(app)
db.init_app(app)
return app
2.2 main/
• init.py
使用了工厂函数之后,只有调用create_app()
之后才会有app
这个变量,因此全局中就没有app,也就不能使用@app.route
定义路由了。Flask提供的蓝本(blueprint)可以解决此问题。
from flask import Blueprint
main = Blueprint('main', __name__)
from . import views, errors
蓝本有一个很大的好处就是,可以把不同的子系统放在不同的蓝本中,从而维护代码的整洁有序。比如app文件夹下可以放两个蓝本,main和auth,分别表示主界面显示和用户注册登录相关,这也是书中下一章介绍的一个例子。
第二行代码中实例化了一个Blueprint对象,两个参数分别是蓝图的名称和蓝图所在的包或模块。
第三行代码写在结尾而不是开头,可以避免与view.py和errors.py的相互导入。
最后,这个蓝本需要在工厂函数中注册到应用实例app上,因此app/_init_.py需要增加两行代码:
def create_app(config_name):
# ...
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
return app
• views.py
''' 视图函数 '''
from datetime import datetime
from flask import render_template, session, redirect, url_for
from . import main
from .forms import NameForm
from .. import db
from ..models import User
@main.route('/', methods=['GET', 'POST']):
def index():
form = NameForm()
if form.validate_on_submit():
# ...
return redirect(url_for('.index'))
# ...
由于使用了蓝本,所以路由使用main.route
而不是app.route
;url_for()函数的参数也变成了main.index
而不是index
,由于蓝本中main可以省略,可以简写成.index
。
• errors.py
这是自定义的错误处理程序,书中有讲,但是由于我上一个文章没有写,这里也就不再赘述了。
• form.py
这个表单模板没有变化,和上一节是一样的。
3. flasky.py
应用实例在这里定义:
import os
from app import create_app, db
from app.models import User, Role
from flask_migrate import Migrate
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
migrate = Migrate(app, db)
@app.shell_context_processor
def make_shell_context():
return dict(db=db, User=User, Role=Role)
运行时,之前程序名称是hello.py
,现在变成了flasky.py
,因此运行时需要将set FLASK_APP = hello.py
做相应更改。
4. 剩余文件
最后还没有讲的,结合项目结构,还剩下:
- migrations/
- test/
- venv
- requirements.txt
migrations/不用手动编写,通过控制台flask db init
,flask db migrate
,flask db upgrade
三步即可完成创建。
test/是单元测试,目前这个应用比较简单,也可以不用编写。
venv/是虚拟环境,和上一节的生成方式也是一致的,通过控制台pip创建。
requirements.txt会存放当前虚拟环境所有的依赖库,同样需要通过控制台生成:
pip freeze > requirements.txt
后期其他的虚拟环境如果想安装这些依赖库,只需要执行:
pip install -r requirements.txt
三、总结
将单一py文件拆分成一个一个独立的文件绝不是简单的事情,涉及了包之间的相互导入,工厂函数create_app()
,蓝本blueprint
,以及单元测试文件。虽然真正编写一个项目肯定是一开始就按照这种多文件的组织形式编写的,我作为一个初学者,顺序则是反过来的。之后学习的路还很长,继续加油。