使用SQLAlchemy正确分离模型和应用程序的Pythonic方法
我很难让我的应用程序运行。每当我尝试在包中分离模块时,Flask SQLAlchemy extension就会创建一个空数据库。为了更好地解释我在做什么,让我展示一下我的项目是如何构建的:使用SQLAlchemy正确分离模型和应用程序的Pythonic方法,python,flask,flask-sqlalchemy,Python,Flask,Flask Sqlalchemy,我很难让我的应用程序运行。每当我尝试在包中分离模块时,Flask SQLAlchemy extension就会创建一个空数据库。为了更好地解释我在做什么,让我展示一下我的项目是如何构建的: Project | |-- Model | |-- __init__.py | |-- User.py | |-- Server | |-- __init__.py | |-- API | |-- __init__.py 想法很简单:我想为我的模型创建一个包,因为我不喜欢在一个包中传播代码,
Project
|
|-- Model
| |-- __init__.py
| |-- User.py
|
|-- Server
| |-- __init__.py
|
|-- API
| |-- __init__.py
想法很简单:我想为我的模型创建一个包,因为我不喜欢在一个包中传播代码,并分离“子”项目(如API),因为将来我将使用蓝图更好地隔离子应用程序
代码非常简单:
首先是模型
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对象的模型导入db
最后,服务器.\uuuu init\uuuu.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)
允许我传递app对象,而无需创建循环引用
问题是,每当我运行这段代码时,sqlite数据库文件都是空的。这让我想到也许Python不会像我想象的那样导入东西。所以我通过删除导入模型并直接在服务器内部创建用户来测试我的理论。。。瞧,成功了
现在我的问题来了:是否有一种“pythonic”方法可以正确地分离我想要的模块,或者我应该将所有模块都放在同一个包中?现在,您已经使用大致相当于“”模式的模式(所谓的Flask文档)设置了应用程序。这是一个烧瓶的想法,不是Python的。它有一些优点,但也意味着您需要使用
init\u app
方法而不是SQLAlchemy构造函数来初始化SQLAlchemy对象。这样做没有什么“错误”,但这意味着您需要在中运行像create\u all()
这样的方法,如果您尝试在main()
方法中运行它,则当前不会这样做
有几种方法可以解决这个问题,但由您决定您想要哪种(没有正确的答案):
不要使用应用程序工厂模式
这样,您就不会在函数中创建应用程序。相反,你把它放在某个地方(比如在project/\uu init\uuuu.py
中)。您的project/\uuuuu init\uuuuuuupy
文件可以导入模型
包,而模型
包可以从项目
导入应用程序
。这是一个循环引用,但只要在model
尝试从package
导入app
之前先在项目
包中创建app
对象,就可以了。有关示例,请参见上的Flask docs,在该示例中,您可以将包拆分为多个包,但这些其他包仍可以通过使用循环引用来使用app
对象。这些文件甚至说:
每个Python程序员都讨厌它们,但我们只是添加了一些:
循环进口。[…]请注意,这是一个
总的来说,这个主意不好,但在这里它实际上是好的
如果执行此操作,则可以更改Models/\uuuu init\uuuu.py
文件,以在构造函数中引用应用程序来构建SQLAlchemy
对象。这样,您就可以使用SQLAlchemy
对象的create_all()
和drop_all()
方法
保持现有状态,但在请求上下文()中构建
如果您继续使用现有功能(在函数中创建应用程序),则需要在模型
包中构建SQLAlchemy
对象,而不使用应用程序
对象作为构造函数的一部分(如您所做)。在主方法中,更改
db = SQLAlchemy(app)
…到
db.init_app(app)
然后,需要将create_all()
方法移动到应用程序上下文中的函数中。在项目的早期,一种常见的方法是使用装饰器
app = Flask(...)
@app.before_first_request
def initialize_database():
db.create_all()
“initialize_database”方法在Flask处理第一个请求之前运行。您也可以通过使用app\u context()
方法在任何时候执行此操作:
app = Flask(...)
with app.app_context():
# This should work because we are in an app context.
db.create_all()
要意识到,如果要继续使用应用程序工厂模式,您应该真正了解应用程序上下文是如何工作的;一开始可能会让人困惑,但有必要了解“应用程序未在db实例上注册,且没有应用程序绑定到当前上下文”等错误的含义。您的问题是这一行:
db = SQLAlchemy(app)
应该是这样的:
db.init_app(app)
通过再次运行SQLAlchemy应用程序,您将db重新分配给新创建的db obj
请尽量不要离开应用程序出厂设置。它消除了导入时间的副作用,是一件好事。事实上,您可能希望在工厂内导入db,因为导入一个对基础进行子类化的模型(在本例中为db.model)有其自身的副作用(但这不是一个问题)
在
\uuuu init\uuuuuuuy.py
中初始化应用程序意味着当您从软件包中导入任何东西以供使用时,您将最终启动应用程序,即使您不需要它。实际上它不会。db=SQLAlchemy(app)是我用正确的app上下文重置db变量的方法。如果我删除这一行,我会得到以下错误:RuntimeError:应用程序未在db实例上注册,并且没有绑定到当前上下文的应用程序。关于大写,你是对的!我会解决这个问题。我的C++/C#背景太强了,运气不好。仍在获取“应用程序未在db实例上注册且没有应用程序绑定到当前上下文”错误。在我看来,一旦db变量被实例化,新的应用程序上下文就没有边界了。这个答案太棒了!在阅读了您编写和链接的所有内容后,我发现我只需要一个简单的with app.test_request_context():调用我的模型之前