Appearance
前几章把代码都写在一个文件里,只适合 demo;真实项目要"保持代码简洁且易于扩展"。本章把 Flask 项目改造成模块化结构:配置文件分离、应用工厂函数、蓝图分模块、Flask-Login 做用户认证,并用手动命令注册管理员。
配置文件(config.py)
把零散的 app.config[...] 整合为配置类,用类继承建立多套环境配置:
python
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config:
SECRET_KEY = 'Chapter6'
SQLALCHEMY_TRACK_MODIFICATIONS = False
@staticmethod
def init_app(app): # 初始化时执行自定义操作(默认空)
pass
class DevelopmentConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'data-test.sqlite')
class ProductionConfig(Config):
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
configs = dict(development=DevelopmentConfig, testing=TestingConfig, production=ProductionConfig)加载配置按环境变量 FLASK_ENV(命令行 set FLASK_ENV=development;PyCharm 默认 development,命令行默认 production):
python
from flask.helpers import get_env
from config import configs
env = get_env()
config = configs.get(env)
app.config.from_object(config)
config.init_app(app)项目结构(模块化)
项目根/
├── manage.py ← 应用管理入口(Manager)
├── config.py ← 配置文件
└── app/ ← Python 包(templates/ static/ 移入其中)
├── __init__.py ← 应用"工厂"函数 create_app(env)
├── database.py ← BaseModel 基类 + DatabaseHelper
├── commands.py ← 管理指令(如 create_admin)
├── user/ ← 用户模块(蓝图)
│ ├── __init__.py models.py views.py forms.py errors.py common.py
└── admin/ ← 后台管理模块(含子模块)
├── __init__.py views.py common.py
└── user/ ← 子模块:用户管理应用"工厂"函数(app/init.py)
把所有初始化(配置、db、Migrate、Bootstrap、蓝图注册)封装成一个函数,不同环境调用即得不同应用:
python
bootstrap = Bootstrap() # 扩展对象在函数外创建,供其他文件引用
db = SQLAlchemy()
dh = DatabaseHelper(db)
def create_app(env):
app = Flask(__name__)
config = configs.get(env)
app.config.from_object(config)
config.init_app(app)
db.init_app(app)
migrate = Migrate(app, db)
migrate.init_app(app, render_as_batch=True)
bootstrap.init_app(app)
from .user import user
app.register_blueprint(user, url_prefix='/user') # 注册蓝图
from .admin import admin, admin_user
app.register_blueprint(admin, url_prefix='/admin')
app.register_blueprint(admin_user, url_prefix='/admin/user')
for cls in dh.get_all_model_classes(): # 模型注册到模板全局
app.add_template_global(cls)
return app
register_blueprint(蓝图, url_prefix='/user')给模块加 URL 前缀,如/user/login。
管理入口(manage.py)
python
from flask_script import Manager, Shell
from app import create_app, db, dh
env = get_env()
app = create_app(env)
manager = Manager(app)
def make_shell_context():
context = dict(db=db, dh=dh)
context.update((cls.__name__, cls) for cls in dh.get_all_model_classes())
return context
manager.add_command("shell", Shell(make_context=make_shell_context))
manager.add_command('db', MigrateCommand)
if __name__ == '__main__':
manager.run()蓝图(Blueprint):模块化开发
蓝图把一组视图函数组合成一个模块,注册到应用后即可按模块组织路由。
python
# user/__init__.py —— 蓝图初始化
from flask import Blueprint
user = Blueprint('user', __name__)
from . import models, views, errors # 引入即注册:模型被 db 捕捉、视图被蓝图捕捉python
# user/views.py —— 视图用蓝图路由装饰器
@user.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm()
if request.method == 'POST' and form.validate_on_submit():
item = UserModel(username=form.username.data, password=form.password.data,
info=UserInfoModel(phone=form.phone.data, email=form.email.data))
try:
dh.session.add(item); dh.session.commit()
flash('注册成功', 'success'); return redirect('login')
except Exception as e:
flash('注册失败 - %s' % e, 'danger'); return abort(500)
return render_template('user/register.html', form=form)子模块:后台管理拆成"用户管理"子模块——admin/__init__.py 里 from .user import user as admin_user,再注册 url_prefix='/admin/user'。
Flask-Login:用户认证
解决"登录后访问其他页面却未登录"的问题——基于 Session 实现用户认证与会话管理:
bash
pip install flask-login==0.4.1python
# app/__init__.py 初始化
from flask_login import LoginManager
login_manager = LoginManager()
def create_app(env):
# ...
login_manager.login_view = 'user.login' # 未登录跳转的视图
login_manager.login_message_category = 'danger'
login_manager.login_message = '请登录后再进行操作'
login_manager.init_app(app)python
# user/models.py —— 模型继承 UserMixin
class UserModel(UserMixin, db.Model, BaseModel):
pass
# user/common.py —— 用户加载器
@login_manager.user_loader
def _load_user(id: int):
return UserModel.query.get(id)
# 视图登录:用 login_user(item) 完成认证(替代手动会话)
from flask_login import login_user
login_user(item)要求认证访问
用 @login_required 装饰器保护视图,未登录自动重定向到登录页;模板中用全局对象 current_user 判断登录态:
python
from flask_login import logout_user, login_required
@user.route('/logout')
@login_required
def logout():
if logout_user():
flash('注销成功', 'success'); return redirect('/user/login')html
{% if current_user.is_anonymous %} 游客菜单:登录/注册 {% else %} {{ current_user.username }} 用户信息/注销 {% endif %}管理员认证访问
包装 login_required,加管理员权限检查:
python
from functools import wraps
from flask_login import login_required
def admin_required(func):
@wraps(func)
def decorated_view(*args, **kwargs):
if not get_current_user().is_admin:
flash('拒绝访问', 'danger'); return abort(403)
return func(*args, **kwargs)
return login_required(decorated_view)所有后台视图加 @admin_required 即可。
管理员注册(管理命令)
用 Flask-Script 命令代替手动改数据库:
python
# app/commands.py
def register_manage_commands(manager: Manager):
@manager.command
def create_admin():
username = input('Input Username:')
password = input('Input Password:')
item = UserModel(username=username, password=password, is_admin=True,
info=UserInfoModel(introduce='Create By Manager.'))
dh.session.add(item); dh.session.commit()bash
python manage.py create_admin # 命令行创建管理员实践建议
- 项目一开始就用工厂函数 + 蓝图组织,别等代码膨胀再重构
- 配置按环境分离:开发/测试/生产用不同
FLASK_ENV与数据库文件 - 需要登录的视图一律
@login_required,别手动判current_user;管理员视图用@admin_required - 扩展对象(db/bootstrap/login_manager)在函数外创建,
init_app在工厂内调用——这是 Flask 扩展的标准用法 - 内置管理操作(建管理员、清缓存)用
@manager.command注册,减少手工操作
一句话总结
项目一大就要拆:配置按环境用类继承分套、create_app(env) 工厂函数装配、蓝图按模块注册并加 url_prefix;扩展对象在函数外创建、init_app 在工厂内调用,需要登录的视图统一 @login_required。