【问题标题】:Pythonic way to correctly separate Model from application using SQLAlchemy使用 SQLAlchemy 将模型与应用程序正确分离的 Pythonic 方法
【发布时间】:2013-10-01 05:48:54
【问题描述】:

我很难让我的应用程序运行。每当我尝试在包中分离模块时,Flask-SQLAlchemy 扩展都会创建一个空数据库。为了更好地解释我在做什么,让我展示一下我的项目的结构:

Project
|
|-- Model
|   |-- __init__.py
|   |-- User.py
|
|-- Server
|   |-- __init__.py
|
|-- API
|   |-- __init__.py

这个想法很简单:我想为我的模型创建一个包,因为我不喜欢在单个包中传播代码,以及单独的“子”项目(如 API),因为我将来会使用蓝图以更好地隔离子应用。

代码很简单:

首先,Model.__init__.py

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()

请注意,我创建它只是为了在包中使用单个 SQLAlchemy() 对象。不,我们去 Model.User

from Model import db

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    Name = db.Column(db.String(80))
    Age = db.Column(db.Integer)
    ...

再次注意我用来允许相同 db 对象的 from Model import db。

最后,Server.__init__.py 是这样的:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import Model, API
db = Model.db


def main():
    app = Flask("__main__")
    db = SQLAlchemy(app)
    db.create_all()
    API.SetAPIHookers(app)
    app.run(host="0.0.0.0", port=5000, debug=True)

if __name__ == "__main__":
    main()

在我看来,db = SQLAlchemy(app) 允许我在不创建循环引用的情况下传递我的应用程序对象。

问题是每当我运行这段代码时,sqlite 数据库文件都会被创建为空的。这让我觉得也许 Python 并没有像我想象的那样导入东西。所以我通过删除导入模型并直接在服务器内部创建用户来测试我的理论......瞧,它奏效了!

现在我的问题来了:有没有一种“pythonic”的方式来正确分离我想要的模块,或者我应该把所有东西都放在同一个包中?

【问题讨论】:

  • 多年后这句话“Python不会像我想象的那样导入东西”仍然是一个问题。在博客、视频甚至课程上教授 Python 的方式与实际工作环境中的 Python 之间似乎仍然存在巨大的差距。我对初学者的建议:不要拘泥于仅展示基本功能如何工作的简单示例;总是更进一步,检查它们在现实世界中的使用方式。但始终检查奖励和成本之间的平衡

标签: python flask flask-sqlalchemy


【解决方案1】:

你的问题是这一行:

db = SQLAlchemy(app)

应该是这样的:

db.init_app(app)

通过再次运行 SQLAlchemy 应用程序,您将 db 重新分配给新创建的 db obj。

请尽量不要离开应用程序工厂设置。它消除了导入时间的副作用,是一件好事。事实上,您可能希望在您的工厂中导入 db,因为导入一个子类化 Base 的模型(在本例中为 db.model)有其自身的副作用(但问题不大)。

__init__.py 中初始化您的应用意味着当您从包中导入任何内容以供使用时,您最终将引导您的应用,即使您不需要它。

【讨论】:

    【解决方案2】:

    现在,您已经使用大致相当于“Application Factory”模式(Flask 文档中如此称呼)的方式设置了您的应用程序。这是一个 Flask 的想法,而不是 Python 的想法。它有一些优点,但这也意味着您需要做一些事情,例如使用init_app 方法而不是 SQLAlchemy 构造函数来初始化您的 SQLAlchemy 对象。这样做并没有什么“错误”,但这意味着您需要在application context 中运行create_all() 之类的方法,如果您尝试在main() 方法中运行它,目前您不会这样做。

    有几种方法可以解决此问题,但由您决定您想要哪一种(没有正确答案):

    不要使用应用工厂模式

    这样,您无需在函数中创建应用程序。相反,你把它放在某个地方(比如project/__init__.py)。你的project/__init__.py 文件可以导入models 包,而models 包可以从project 导入app。这是一个循环引用,但只要在model 尝试从package 导入app 之前首先在project 包中创建app 对象就可以了。请参阅Larger Application Patterns 上的 Flask 文档,以获取可以将包拆分为多个包的示例,但仍然让这些其他包能够通过使用循环引用来使用 app 对象。文档甚至说:

    每个 Python 程序员都讨厌它们,但我们只是添加了一些: 循环进口。 [...] 请注意,这是一个 总的来说是个坏主意,但在这里实际上很好。

    如果您这样做,那么您可以更改您的Models/__init__.py 文件以在构造函数中使用对应用程序的引用来构建SQLAlchemy 对象。这样,您可以使用SQLAlchemy 对象as described in the documentation for Flask-SQLAlchemycreate_all()drop_all() 方法。

    保持你现在的样子,但在 request_context() 中构建

    如果您继续现有的(在函数中创建应用程序),那么您需要在 Models 包中构建 SQLAlchemy 对象,而不使用 app 对象作为构造函数的一部分(正如你所做的那样)。在您的主要方法中,更改...

    db = SQLAlchemy(app)
    

    ...到...

    db.init_app(app)
    

    然后,您需要将create_all() 方法移动到应用程序上下文内的函数中。在项目早期,一个常见的方法是使用 before_first_request() 装饰器....

    app = Flask(...)
    
    @app.before_first_request
    def initialize_database():
        db.create_all()
    

    “initialize_database”方法在 Flask 处理第一个请求之前运行。您也可以随时使用app_context() 方法执行此操作:

    app = Flask(...)
    with app.app_context():
        # This should work because we are in an app context.
        db.create_all()
    

    意识到,如果您要继续使用应用程序工厂模式,您应该真正了解应用程序上下文是如何工作的;一开始可能会令人困惑,但有必要了解“应用程序未在数据库实例上注册并且没有应用程序绑定到当前上下文”之类的错误是什么意思。

    【讨论】:

    • 其实不会。 db = SQLAlchemy(app) 是我用正确的应用程序上下文重置 db 变量的方法。如果我删除此行,则会收到以下错误:RuntimeError: application not registered on db instance and no application bound to current context.
    • 关于大写,你是对的!我会解决这个问题。我的 C++/C# 背景对我来说太强了。
    • 不走运。仍然收到“应用程序未在数据库实例上注册并且没有应用程序绑定到当前上下文”错误。在我看来,一旦 db 变量被实例化,新的应用程序上下文就没有界限了。
    • 惊人的答案!在阅读了您编写和链接的所有内容后,我发现我只需要一个简单的 app.test_request_context():在调用我的 Model.db.create_all() 之前。我还重写了它以更好地适应多应用场景。谢谢!!
    • 谷歌搜索一个小时后,终于找到答案了!
    猜你喜欢
    • 1970-01-01
    • 2015-08-11
    • 2012-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-15
    • 2018-09-06
    相关资源
    最近更新 更多