FLASK|完整版学习(ALL)

flask-前和后打基础,然后看本篇会比较好懂。

一、环境准备与基础配置

1. 导入必要模块

python 复制代码
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import datetime

知识延展

  • Flask是微框架的核心,所有路由、配置都围绕它展开
  • SQLAlchemy是Python最著名的ORM工具,Flask-SQLAlchemy是其Flask集成版
  • Flask-Migrate基于Alembic,解决数据库结构变更问题(如添加字段)
  • 密码永远不要明文存储!werkzeug提供的哈希函数使用PBKDF2算法
  • datetime.utcnow()获取UTC时间,避免时区问题(生产环境建议用datetime.now(timezone.utc)

2. 创建应用实例

python 复制代码
app = Flask(__name__)  

知识延展

  • __name__参数帮助Flask确定模板、静态文件的根目录
  • 生产环境中通常使用工厂模式(create_app()函数)替代直接实例化
  • 可通过app.config.from_object()加载配置类,实现环境隔离

二、数据库配置详解

python 复制代码
# 数据库配置
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:password@localhost:3306/flask_db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'your-secret-key-here'

知识延展

  • 数据库URI支持多种数据库:sqlite:///app.db, postgresql://..., mongodb://...
  • 敏感配置应通过环境变量管理:app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL')
  • SECRET_KEY生成方法:import secrets; secrets.token_hex(32)
  • 其他常用配置:
    • SQLALCHEMY_ECHO=True:打印SQL语句用于调试
    • SQLALCHEMY_POOL_SIZE=10:连接池大小调整

三、初始化扩展组件

python 复制代码
db = SQLAlchemy(app)
migrate = Migrate(app, db)

知识延展

  • 工厂模式下需延迟初始化:db = SQLAlchemy()然后在create_app()db.init_app(app)
  • Migrate对象本身不提供命令,需配合flask-migrate包使用CLI命令
  • 初始化顺序很重要:必须先有appdb才能创建migrate
代码片段 db 的作用 类比
class User(db.Model) 定义蓝图:声明这是一个数据库表模型 建筑图纸
db.Column(...) 定义结构:规定列的类型和约束 砖块、水泥规格
db.session.add() 准备动作:把数据放入暂存区 把家具搬进仓库等待入库
db.session.commit() 执行动作:永久保存数据到硬盘 正式办理入库手续
User.query... 读取数据:从数据库查找信息 去仓库查询库存
db.create_all() 初始化:根据蓝图建造实际建筑 施工队按图纸盖楼

四、定义数据模型(ORM核心)

User模型详解

python 复制代码
class User(db.Model):
    __tablename__ = 'users'
    
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password_hash = db.Column(db.String(256), nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    
    # 关系
    posts = db.relationship('Post', backref='author', lazy=True)
    
    def set_password(self, password):
        self.password_hash = generate_password_hash(password)
    
    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

知识延展

  • 列类型映射:String→VARCHAR, Text→TEXT, Integer→INT, DateTime→DATETIME
  • 约束类型:unique=True, nullable=False, index=True, default=value
  • 关系加载策略:
    • lazy='select':单独查询(默认)
    • lazy='joined':JOIN查询
    • lazy='dynamic':返回Query对象用于过滤
  • 密码处理最佳实践:
    • 永远不要存储明文密码
    • 可添加盐值(generate_password_hash自动处理)
    • 考虑添加密码强度验证

Post模型详解

python 复制代码
class Post(db.Model):
    __tablename__ = 'posts'
    
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    content = db.Column(db.Text, nullable=False) 
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    # 外键关联users表的id字段,建立一对多关系

知识延展

  • 外键约束:db.ForeignKey('table.column'),删除时可设ondelete='CASCADE'
  • 复合主键:primary_key=True可在多个列上设置
  • 索引优化:对频繁查询的列添加index=True
  • 模型继承:可创建抽象基类共享字段(如TimestampMixin

五、数据库初始化命令

python 复制代码
@app.cli.command('init-db')  # 注册自定义CLI命令:flask init-db
def init_db():
    db.create_all()  # 创建所有未存在的表(不会更新已有表结构)
    print('数据库初始化完成!')

知识延展

  • db.create_all()仅适用于开发环境,生产环境必须用迁移
  • 迁移工作流:
    1. flask db init(仅一次)
    2. flask db migrate -m "描述"
    3. flask db upgrade
  • 自定义CLI命令可带参数:@app.cli.argument('name')
  • 数据种子:可创建seed-db命令插入测试数据

六、CRUD操作实战

1. 创建用户(POST)

python 复制代码
@app.route('/users', methods=['POST'])
def create_user():
    data = request.get_json()
    user = User(username=data['username'], email=data['email'])
    user.set_password(data['password'])
    db.session.add(user)
    db.session.commit()
    return jsonify({'id': user.id}), 201

知识延展

  • 输入验证:应使用marshmallowpydantic验证数据
  • 错误处理:捕获IntegrityError处理唯一约束冲突
  • 异步支持:Flask 2.0+支持async def视图函数
  • 状态码规范:
    • 200:成功
    • 201:资源创建成功
    • 400:请求错误
    • 404:未找到
    • 500:服务器错误

2. 查询用户(GET)

python 复制代码
@app.route('/users/<int:id>')
def get_user(id):
    user = User.query.get_or_404(id)
    return jsonify({
        'id': user.id,
        'username': user.username,
        'email': user.email
    })

知识延展

  • 查询方法:
    • User.query.get(id):主键查询
    • User.query.filter_by(username='abc'):条件查询
    • User.query.filter(User.age > 18):复杂条件
  • 分页查询:User.query.paginate(page=1, per_page=10)
  • 序列化优化:使用Marshmallow定义Schema自动转换

3. 更新用户(PUT)

python 复制代码
@app.route('/users/<int:id>', methods=['PUT'])
def update_user(id):
    user = User.query.get_or_404(id)
    data = request.get_json()
     
    # 使用get方法提供默认值,避免KeyError
    user.username = data.get('username', user.username)
    user.email = data.get('email', user.email)
    
    # 如需更新密码:user.set_password(data['password'])
    
    db.session.commit()
    return jsonify({'message': '更新成功'})

知识延展

  • PATCH vs PUT:PATCH部分更新,PUT全量替换
  • 乐观锁:添加版本号字段防止并发覆盖
  • 审计日志:记录谁在何时修改了什么
  • 字段白名单:只允许更新特定字段,防止越权

4. 删除用户(DELETE)

python 复制代码
@app.route('/users/<int:id>', methods=['DELETE'])
def delete_user(id):
    user = User.query.get_or_404(id)
    
    # 注意:如果有外键约束,需先处理关联数据 
    # 方法1:级联删除(模型中设置cascade='all, delete-orphan')
    # 方法2:手动删除关联帖子
    # for post in user.posts: db.session.delete(post)
    
    db.session.delete(user)
    db.session.commit()
    return jsonify({'message': '删除成功'})

知识延展

  • 软删除:添加deleted_at字段代替物理删除
  • 外键策略:
    • ondelete='CASCADE':自动删除子记录
    • ondelete='SET NULL':子记录外键设为NULL
  • 事务管理:使用with db.session.begin()自动回滚
  • 批量操作:User.query.filter(...).delete()

4. 测试API

bash 复制代码
# 创建用户
curl -X POST http://localhost:5000/users \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","email":"alice@example.com","password":"secure123"}'

# 查询用户
curl http://localhost:5000/users/1

-X POST:

-X: 指定 HTTP 请求方法 (Request Method)。

-H: 代表 Header (请求头)。

-d: 代表 Data (数据)。

八、生产环境优化相关

  1. 配置管理 :使用.env文件+python-dotenv管理不同环境配置
  2. 日志系统 :集成logging模块记录关键操作
  3. API文档 :使用Flask-RESTXSwagger自动生成文档
  4. 认证授权:添加JWT或OAuth2.0认证机制
  5. 性能优化
    • 数据库连接池调优
    • 查询结果缓存(Redis)
    • 异步任务(Celery)
  6. 安全防护
    • SQL注入防护(ORM已内置)
    • XSS防护(自动转义)
    • 速率限制(Flask-Limiter

九、数据库迁移:管理表结构的生命周期

在生产环境中,直接修改数据库表结构是危险的。Flask-Migrate (基于Alembic) 允许我们像管理代码版本一样管理数据库结构。

命令详解与原理

bash 复制代码
# 初始化迁移
flask db init

# 创建迁移脚本
flask db migrate -m "Initial migration"

# 应用迁移
flask db upgrade

# 回滚迁移
flask db downgrade

知识延展

  • 为什么需要迁移? db.create_all() 只能建表不能改表(如添加字段会报错),而迁移脚本可以安全地执行 ALTER TABLE
  • 手动修正 :自动生成的脚本有时不完美(如默认值处理),需人工检查 migrations/versions/*.py 文件。
  • 多数据库支持:Alembic支持配置多个绑定(binds),可同时迁移MySQL和SQLite。
  • 数据迁移:除了结构变更,还可以在迁移脚本中编写Python代码来清洗或转换旧数据。

十、用户认证系统:Flask-Login 深度解析

会话管理是Web应用的核心。Flask-Login 提供了用户会话管理的便捷接口,处理登录、登出、记住我等功能。

1. 初始化与配置

python 复制代码
from flask import Flask, render_template, redirect, url_for, request, flash
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'  # 必须设置,用于加密Cookie会话
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'

db = SQLAlchemy(app)

# 初始化LoginManager
login_manager = LoginManager(app)
login_manager.login_view = 'login'  # 未登录用户访问受保护页面时重定向的路由端点
# login_manager.login_message = "请先登录" # 自定义提示消息

知识延展

  • Session机制 :Flask-Login默认使用Cookie存储会话ID,服务器端通过SECRET_KEY验证签名。
  • Remember Me :可通过login_user(user, remember=True)实现"记住我"功能, Cookie有效期更长。
  • 加载器@login_manager.user_loader 是必须的,用于根据Session中的ID重新加载用户对象。

2. 用户模型设计

python 复制代码
# 用户模型必须继承 UserMixin,它提供了 is_authenticated, is_active, is_anonymous, get_id 等属性
class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    password_hash = db.Column(db.String(256))

@login_manager.user_loader
def load_user(user_id):
    # 根据ID查询用户,返回User对象或None
    # 注意:user_id从Session传来是字符串,需转为int
    return User.query.get(int(user_id))

知识延展

  • UserMixin的作用 :避免我们手动编写四个标准属性方法。如果需要自定义逻辑(如判断账号是否被冻结),可重写is_active
  • 匿名用户的处理 :未登录时 current_user 是一个 AnonymousUserMixin 实例,is_authenticated 为 False。

3. 注册逻辑(Register)

python 复制代码
@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        # 获取表单数据(实际生产中建议使用 Flask-WTF)
        username = request.form['username']
        password = request.form['password']
        
        # 检查用户名是否存在
        if User.query.filter_by(username=username).first():
            flash('用户名已存在') # 闪现消息,下次请求时显示并消失
            return redirect(url_for('register'))
        
        # 创建新用户
        user = User(username=username)
        user.password_hash = generate_password_hash(password) # 密码哈希处理
        db.session.add(user)
        db.session.commit()
        
        flash('注册成功,请登录')
        return redirect(url_for('login'))
    
    return render_template('register.html')

知识延展

  • Flash消息 :配合模板中的 get_flashed_messages() 使用,适合一次性通知(如"注册成功")。
  • 安全性:此处缺少输入验证(长度、特殊字符),生产环境必须加验证逻辑防止XSS或注入。

4. 登录与登出(Login & Logout)

python 复制代码
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user = User.query.filter_by(username=username).first()
        
        # 验证用户存在 且 密码哈希匹配
        if user and check_password_hash(user.password_hash, password):
            login_user(user) # 关键步骤:创建会话
            
            # 可选:处理 next 参数(用户尝试访问受保护页面被重定向来的地址)
            # next_page = request.args.get('next')
            # return redirect(next_page or url_for('dashboard'))
            
            return redirect(url_for('dashboard'))
        
        flash('用户名或密码错误')
    
    return render_template('login.html')

@app.route('/logout')
@login_required # 装饰器:只有登录用户才能访问此路由
def logout():
    logout_user() # 清除会话
    return redirect(url_for('index'))

知识延展

  • @login_required :如果未登录,自动重定向到 login_manager.login_view 指定的页面,并附带 next 参数。
  • CSRF保护 :原生 request.form 没有CSRF保护,建议结合 Flask-WTF 使用。

5. 受保护的资源

python 复制代码
@app.route('/dashboard')
@login_required
def dashboard():
    # current_user 是全局代理,代表当前登录的用户对象
    return f'欢迎,{current_user.username}!'

@app.route('/')
def index():
    return render_template('index.html')

十一、RESTful API 开发:令牌认证与资源管理

构建前后端分离应用时,我们需要标准的API接口和无状态的认证机制(如Token)。

1. 基础配置与CORS

python 复制代码
from flask import Flask, jsonify, request, abort
from flask_httpauth import HTTPTokenAuth
from flask_cors import CORS # 跨域资源共享扩展

app = Flask(__name__)
CORS(app)  # 允许所有域名访问API,生产环境应限制特定域名
# CORS(app, resources={r"/api/*": {"origins": "https://myfrontend.com"}})

auth = HTTPTokenAuth(scheme='Bearer') # 定义使用 Bearer Token 方案

知识延展

  • CORS原理 :浏览器出于安全限制不同域名的AJAX请求,服务器需返回 Access-Control-Allow-Origin 头。
  • 认证方案 :除了Bearer Token,还有Basic Auth (HTTPTokenAuth(scheme='Basic')),但Token更适合移动端和SPA。

2. Token 验证逻辑

python 复制代码
# 模拟数据库(实际应查库)
users = {
    "user1": "password1",
    "user2": "password2"
}
tokens = {} # 内存存储Token,重启后失效,生产环境请用Redis

@auth.verify_token
def verify_token(token):
    # Flask-HTTPAuth 调用此函数验证Token有效性
    # 如果有效,返回用户标识(如username);无效返回None
    if token in tokens:
        return tokens[token]
    return None

知识延展

  • JWT vs 普通Token :示例使用的是随机UUID存服务器(有状态)。生产环境推荐 JWT (JSON Web Tokens),它将用户信息编码在Token本身,服务器无需存储,实现真正的无状态。
  • Token过期:需在Token中加入时间戳,验证时检查是否过期。

3. 获取 Token 接口

python 复制代码
@app.route('/api/token', methods=['POST'])
def get_token():
    auth_data = request.get_json()
    username = auth_data.get('username')
    password = auth_data.get('password')
    
    # 简单验证
    if username in users and users[username] == password:
        import uuid
        token = str(uuid.uuid4())
        tokens[token] = username # 建立映射
        return jsonify({'token': token}), 200
    
    abort(401) # 返回 401 Unauthorized

知识延展

  • HTTPS强制:传输密码和Token必须使用HTTPS,否则会被中间人窃听。
  • 密码策略:实际项目中密码应对比哈希值,而非明文。

4. RESTful 资源操作 (CRUD)

python 复制代码
resources = [
    {'id': 1, 'name': '资源1', 'description': '描述1'},
    {'id': 2, 'name': '资源2', 'description': '描述2'},
]

# GET /api/resources - 获取列表
@app.route('/api/resources', methods=['GET'])
@auth.login_required # 需要Token认证
def get_resources():
    return jsonify({'resources': resources})

# GET /api/resources/<id> - 获取单个
@app.route('/api/resources/<int:resource_id>', methods=['GET'])
@auth.login_required
def get_resource(resource_id):
    # 使用生成器表达式查找
    resource = next((r for r in resources if r['id'] == resource_id), None)
    if resource is None:
        abort(404) # 资源不存在
    return jsonify(resource)

# POST /api/resources - 创建
@app.route('/api/resources', methods=['POST'])
@auth.login_required
def create_resource():
    data = request.get_json()
    # 简单校验
    if 'name' not in data:
        abort(400, description="Missing 'name' field")
        
    new_resource = {
        'id': len(resources) + 1,
        'name': data['name'],
        'description': data.get('description', '')
    }
    resources.append(new_resource)
    return jsonify(new_resource), 201 # 201 Created

# PUT /api/resources/<id> - 全量/部分更新
@app.route('/api/resources/<int:resource_id>', methods=['PUT'])
@auth.login_required
def update_resource(resource_id):
    resource = next((r for r in resources if r['id'] == resource_id), None)
    if resource is None:
        abort(404)
    
    data = request.get_json()
    resource['name'] = data.get('name', resource['name'])
    resource['description'] = data.get('description', resource['description'])
    
    return jsonify(resource)

# DELETE /api/resources/<id> - 删除
@app.route('/api/resources/<int:resource_id>', methods=['DELETE'])
@auth.login_required
def delete_resource(resource_id):
    global resources
    initial_count = len(resources)
    resources = [r for r in resources if r['id'] != resource_id]
    
    if len(resources) == initial_count:
        abort(404)
        
    return jsonify({'result': True})

知识延展

  • HTTP动词语义
    • POST: 创建新资源。
    • PUT: 替换整个资源(或部分更新,视API设计而定,严格来说PATCH用于部分更新)。
    • DELETE: 删除资源。
  • 状态码规范
    • 200 OK: 成功。
    • 201 Created: 创建成功。
    • 204 No Content: 删除成功(通常不返回body)。
    • 400 Bad Request: 参数错误。
    • 401 Unauthorized: 未认证。
    • 403 Forbidden: 已认证但无权限。
    • 404 Not Found: 资源不存在。

5. 统一错误处理

python 复制代码
@app.errorhandler(404)
def not_found(error):
    # 统一返回JSON格式错误,而非HTML
    return jsonify({'error': 'Not found', 'message': str(error)}), 404

@app.errorhandler(401)
def unauthorized(error):
    return jsonify({'error': 'Unauthorized', 'message': 'Invalid token or missing'}), 401

十二、表单处理:Flask-WTF 的安全与验证

手动处理 request.form 既繁琐又不安全。Flask-WTF 集成了 CSRF 保护和强大的数据验证功能。

1. 定义表单类

python 复制代码
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, TextAreaField
# 导入验证器
from wtforms.validators import DataRequired, Email, Length, EqualTo

# 登录表单
class LoginForm(FlaskForm):
    # validators 列表按顺序执行,任意一个失败则表单无效
    email = StringField('邮箱', validators=[DataRequired(), Email()])
    password = PasswordField('密码', validators=[DataRequired(), Length(min=6)])
    submit = SubmitField('登录')

# 注册表单
class RegisterForm(FlaskForm):
    username = StringField('用户名', validators=[DataRequired(), Length(min=3, max=20)])
    email = StringField('邮箱', validators=[DataRequired(), Email()])
    password = PasswordField('密码', validators=[DataRequired(), Length(min=6)])
    # EqualTo 确保两次密码输入一致
    confirm_password = PasswordField('确认密码', validators=[DataRequired(), EqualTo('password', message='两次密码必须一致')])
    submit = SubmitField('注册')

知识延展

  • CSRF TokenFlaskForm 自动生成 csrf_token 隐藏字段,防止跨站请求伪造攻击。
  • 自定义验证器 :可继承 Validator 类编写逻辑(如检查用户名是否在黑名单中)。
  • 文件上传 :使用 FileFieldFileAllowed 验证器处理上传。

2. 视图函数集成

python 复制代码
@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    # validate_on_submit() 检查:1. 请求是POST 2. CSRF验证通过 3. 所有字段验证通过
    if form.validate_on_submit():
        # 访问清洗后的数据
        email = form.email.data
        # 此处添加实际登录逻辑...
        flash(f'登录成功:{email}', 'success')
        return redirect(url_for('index'))
    
    # GET请求或验证失败时渲染模板,form中包含错误信息
    return render_template('login.html', form=form)

3. 模板渲染 (Jinja2)

html 复制代码
<form method="POST">
    {{ form.hidden_tag() }} 
    <!-- 自动生成 <input type="hidden" name="csrf_token" ...> -->
    
    <div>
        {{ form.email.label }}
        {{ form.email() }} 
        <!-- 遍历错误列表显示 -->
        {% for error in form.email.errors %}
            <span style="color: red;">{{ error }}</span>
        {% endfor %}
    </div>
    
    <div>
        {{ form.password.label }}
        {{ form.password() }}
        {% for error in form.password.errors %}
            <span style="color: red;">{{ error }}</span>
        {% endfor %}
    </div>
    
    {{ form.submit() }}
</form>

知识延展

  • Bootstrap集成 :可使用 Flask-BootstrapWTForms-Bootstrap 自动渲染美观的表单结构。
  • 宏(Macros):在Jinja2中定义宏来复用表单字段的渲染逻辑(Label + Input + Error)。

十三、项目结构最佳实践:实际应用

随着代码量增加,将所有代码放在 app.py 会导致难以维护。我们需要采用应用工厂模式蓝图(Blueprints)

1. 推荐的目录结构

text 复制代码
flask_project/
├── app/
│   ├── __init__.py          # 【核心】应用工厂函数 create_app
│   ├── models.py            # 数据库模型 (User, Post...)
│   ├── routes/              # 路由模块化
│   │   ├── __init__.py
│   │   ├── main.py          # 首页、关于等通用路由
│   │   ├── auth.py          # 登录、注册、登出
│   │   └── api.py           # REST API 接口
│   ├── templates/           # HTML 模板
│   │   ├── base.html        # 基类模板 (包含导航栏、footer)
│   │   ├── auth/            # 认证相关模板
│   │   └── main/            # 主页面模板
│   ├── static/              # 静态资源
│   │   ├── css/
│   │   ├── js/
│   │   └── images/
│   ├── forms.py             # WTForms 表单类
│   └── utils.py             # 辅助函数 (如发送邮件、格式化时间)
├── migrations/              # 数据库迁移脚本 (由 flask-migrate 生成)
├── tests/                   # 单元测试 (pytest/unittest)
├── config.py                # 配置类 (Dev, Prod, Test)
├── requirements.txt         # 依赖包列表
├── .env                     # 环境变量 (不要提交到Git!)
├── .gitignore
└── run.py                   # 入口脚本

知识延展

  • 解耦:路由、模型、表单分离,便于多人协作和单元测试。
  • 蓝图(Blueprint)auth_bp, api_bp 允许我们在不同模块定义路由,最后统一注册到主应用。

2. 应用工厂模式 (app/__init__.py)

这是现代Flask项目的核心,解决了循环导入问题,并支持多实例测试。

python 复制代码
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_migrate import Migrate
from config import Config # 导入配置类

# 初始化扩展实例(此时未绑定app)
db = SQLAlchemy()
login_manager = LoginManager()
migrate = Migrate()

def create_app(config_class=Config):
    app = Flask(__name__)
    
    # 加载配置
    app.config.from_object(config_class)
    
    # 初始化扩展并绑定app
    db.init_app(app)
    login_manager.init_app(app)
    migrate.init_app(app, db)
    
    # 配置 LoginManager
    login_manager.login_view = 'auth.login' # 指向 auth 蓝图的路由
    
    # 注册蓝图
    # 导入必须在函数内部,避免循环导入
    from app.routes.main import main_bp
    from app.routes.auth import auth_bp
    from app.routes.api import api_bp
    
    app.register_blueprint(main_bp) # 根路径
    app.register_blueprint(auth_bp, url_prefix='/auth') # /auth/login, /auth/register
    app.register_blueprint(api_bp, url_prefix='/api')   # /api/resources
    
    return app

知识延展

  • 循环导入问题 :如果在文件顶部 from app import app,而 app 又导入 routesroutes 又导入 app,就会报错。工厂模式将 app 的创建推迟到函数内部,解决了这个问题。
  • 测试友好 :可以在测试代码中调用 create_app(TestConfig) 创建独立的测试实例,不影响开发环境。

3. 配置文件管理 (config.py)

使用类继承和环境变量管理不同环境的配置。

python 复制代码
import os
from datetime import timedelta

class Config:
    # 密钥优先从环境变量读取,防止硬编码泄露
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-key-fallback'
    
    # 数据库URL
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///app.db'
    
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    
    # 会话永久有效期
    PERMANENT_SESSION_LIFETIME = timedelta(days=7)

class DevelopmentConfig(Config):
    DEBUG = True
    # 开发环境可开启 SQL 打印
    # SQLALCHEMY_ECHO = True

class ProductionConfig(Config):
    DEBUG = False
    # 生产环境应使用强随机密钥和正式数据库
    pass

class TestingConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:' # 测试用内存数据库

# 配置字典,方便通过字符串选择
config = {
    'development': DevelopmentConfig,
    'production': ProductionConfig,
    'testing': TestingConfig,
    'default': DevelopmentConfig
}

启动脚本 (run.py):

python 复制代码
import os
from app import create_app, db
from app.models import User  # 导入模型以便 shell 中使用

app = create_app(os.getenv('FLASK_CONFIG') or 'default')

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

Tips:完整实例之后会单独发表在资源栏目,敬请期待(*❦ω❦)

相关推荐
航Hang*2 小时前
第2章:进阶Linux系统——第1节:配置与管理Samba服务器
linux·运维·服务器·笔记·学习
HyperAI超神经2 小时前
在线教程丨免费CPU资源快速部署,覆盖Qwen3.5/DeepSeek-R1/Gemma 3/Llama 3.2等热门开源模型
人工智能·深度学习·学习·机器学习·ai编程·llama·vllm
郝学胜-神的一滴2 小时前
深度学习:CNN 与 RNN——解锁多模态处理能力
人工智能·python·rnn·深度学习·神经网络·cnn
稽稽稽稽不如人2 小时前
《从零开始的java从入门到入土的学习生活——JavaWeb后端篇》Chapter15——JavaWeb后端篇学习记录——多表关系、多表查询、分页查询
学习·生活
今天你TLE了吗2 小时前
JVM学习笔记:第九章——StringTable字符串常量池
java·jvm·笔记·后端·学习
Amazing_Cacao2 小时前
品鉴师高级|全局判断成体系(精品可可,精品巧克力)
笔记·学习
西野.xuan2 小时前
【effective c++】条款四十三:学习处理模版化基类内的名称
java·c++·学习
七月初七772 小时前
使用Python连接MySQL数据库
数据库·python·mysql
测试19982 小时前
自动化测试:selenium详解
自动化测试·软件测试·python·selenium·测试工具·职场和发展·测试用例