14

我很难让我的应用程序运行。每当我尝试在包中分离模块时,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”的方式来正确分离我想要的模块,或者我应该把所有东西都放在同一个包中?

4

2 回答 2

26

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

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

不要使用应用程序工厂模式

这样,您就不会在函数中创建应用程序。相反,你把它放在某个地方(比如 in project/__init__.py)。您的project/__init__.py文件可以导入models包,而models包可以appproject. 这是一个循环引用,但只要appproject尝试modelapp. package请参阅有关较大应用程序模式的 Flask 文档,以获取可以将包拆分为多个包的示例,但仍然让这些其他包能够app通过使用循环引用来使用该对象。文档甚至说:

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

如果这样做,则可以更改Models/__init__.py文件以SQLAlchemy在构造函数中使用对应用程序的引用来构建对象。这样,您可以使用对象的create_all()drop_all()方法,如 Flask-SQLAlchemy 的文档中所述SQLAlchemy

保持现在的状态,但在 request_context() 中构建

如果您继续现有的(在函数中创建应用程序),那么您将需要SQLAlchemy在包中构建对象,Models而不使用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()

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

于 2013-09-25T14:57:47.780 回答
5

你的问题是这一行:

db = SQLAlchemy(app)

应该是这样的:

db.init_app(app)

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

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

初始化你的应用程序__init__.py意味着当你从你的包中导入任何东西以供使用时,你最终会引导你的应用程序,即使你不需要它。

于 2014-06-23T23:43:24.290 回答