You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask create_app工厂模式下,模型类无法访问自定义数据库连接的解决

解决Flask应用工厂模式下自定义数据库封装的访问问题

我明白你现在遇到的困扰了——用Flask的应用工厂模式(create_app)构建项目时,自定义的数据库封装没办法像SQLAlchemy那样轻松在model文件里调用,核心问题在于:你在create_app里创建的db实例是局部变量,models/user.py里的UserModel根本拿不到它;如果直接在model里实例化Database,又没有正确的配置参数,自然会连接失败。

下面给你几种实用的解决方案,按照Flask的设计习惯推荐第一种:

方案一:将数据库实例绑定到Flask App对象(最推荐)

这种方式贴合Flask的上下文设计,把数据库实例挂载到app对象上,再通过current_app代理在model中访问。

步骤1:修改app.py,绑定db到app

在create_app函数中,创建完db实例后,把它绑定到app对象上:

def create_app(debug=True):
    config = LocalConfig if debug else LiveConfig
    app = Flask(__name__)
    app.config.from_object(config)
    # ... 其他配置代码 ...
    
    # 创建数据库实例并绑定到app
    db = Database(
        host=app.config['MYSQL_HOST'],
        db=app.config['MYSQL_DB'],
        user=app.config['MYSQL_USER'],
        passwd=app.config['MYSQL_PASS'],
    )
    app.db = db  # 把db挂载到app属性上
    
    register_decorators(app)
    register_extensions(app)
    register_endpoints(api)
    return app

步骤2:修改models/user.py,通过current_app获取db

导入Flask的current_app代理,在model方法中调用绑定的数据库实例:

import datetime
import json
import logging
from flask import current_app  # 导入current_app

class UserModel:
    @classmethod
    def get_by_username(cls, username=None):
        # 从当前app上下文获取数据库实例
        user = current_app.db.getOne(
            table='users',
            fields=['user_id','data'],
            where=('username = %s', [username])
        )
        if user:
            user['data'] = json.loads(user['data'])
        return user

说明:

current_app是Flask提供的上下文代理,只要在请求上下文或应用上下文存在的环境中(比如接口请求、脚本中手动推送上下文),它就会指向当前的Flask app实例,完美适配应用工厂模式。如果需要在非请求环境(比如脚本)中调用model,可以手动推送上下文:

from api.app import create_app
app = create_app()
with app.app_context():
    # 在这里调用UserModel的方法
    UserModel.get_by_username("test")

方案二:让Database类实现单例模式

通过单例模式确保整个应用中只有一个Database实例,create_app中初始化一次后,model里获取的就是同一个配置好的实例。

修改common/database.py实现单例:

import MySQLdb

class Database:
    _instance = None  # 存储单例实例
    conn = None
    cur = None
    conf = None

    def __new__(cls, **kwargs):
        # 只创建一次实例
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, **kwargs):
        # 仅第一次初始化时配置连接
        if self.conf is None:
            self.conf = kwargs
            self.conf['keep_alive'] = kwargs.get('keep_alive', False)
            self.conf['charset'] = kwargs.get('charset', 'utf8')
            self.conf['host'] = kwargs.get('host', 'localhost')
            self.conf['port'] = kwargs.get('port', 3306)
            self.conf['autocommit'] = kwargs.get('autocommit', False)
            self.conf['ssl'] = kwargs.get('ssl', False)
            self.connect()

    # ... 原有的connect、getOne等方法保持不变 ...

修改models/user.py使用单例:

import datetime
import json
import logging
from api.common.database import Database

class UserModel:
    # 直接获取单例实例(create_app中已经初始化过配置)
    db = Database()

    @classmethod
    def get_by_username(cls, username=None):
        user = cls.db.getOne(
            table='users',
            fields=['user_id','data'],
            where=('username = %s', [username])
        )
        if user:
            user['data'] = json.loads(user['data'])
        return user

说明:

这种方式适合简单场景,但要注意:如果你的应用是多线程/多进程部署,当前的conn是实例属性,可能会出现连接共享的问题,需要额外处理连接的线程隔离(比如每次请求前检查连接状态,或者为每个线程创建独立连接)。

方案三:依赖注入(耦合度较高,不推荐)

在资源层把数据库实例传递给model方法,虽然能解决问题,但会增加代码耦合度:

# resources/user.py 中修改post方法
def post(self):
    args = self.reqparse.parse_args()
    # 从current_app获取db并传给model
    user = UserModel.get_by_username(args['username'], db=current_app.db)
    return {'message': 'Wrong credentials'}

# models/user.py 中修改方法
@classmethod
def get_by_username(cls, username=None, db=None):
    user = db.getOne(
        table='users',
        fields=['user_id','data'],
        where=('username = %s', [username])
    )
    if user:
        user['data'] = json.loads(user['data'])
    return user

内容的提问来源于stack exchange,提问作者stwhite

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:07:49