Flask_SQLAlchemy、db.create_all() 在通过服务 class 导入时无法 "see" 我的表

Flask_SQLAlchemy, db.create_all() is unable to "see" my tables when imported though a service class

意图:将我的代码重构为 MVC(这只是 model/database 部分),并让服务器在第一个 运行 上创建带有 table 的数据库,如果数据库或者 tables 不存在。 这在使用包含该文件中定义的所有 classes 和函数的“平面”文件时有效,但是在将函数移出到服务 class 并将模型移到它们自己的文件夹中后,模型 classes,db.create_all() 函数似乎无法再正确检测到 table class。

示例结构(最小可行问题):

server.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.sqlite'
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)


def main():
    # Intentionally moved into the main() function to prevent import loops
    from services.users import UserService
    users = UserService(db)
    db.create_all()
    app.run(debug=True)


if __name__ == '__main__':
    main()

services\users.py

# Class used to access users data using a session

from models.users import Users


class UserService:
    def __init__(self, db):
        self.db = db

    def get_all(self):
        return self.db.session.query(Users).all()

    def get(self, uid):
        return self.db.session.query(Users).get(uid)

    def add(self, json):
        user = Users(email=json['email'], password=json['password'])

        self.db.session.add(user)
        self.db.session.commit()

        return user

models\users.py

# The actual model

from server import db


class Users(db.Model):
    _id = db.Column("id", db.Integer, primary_key=True)
    email = db.Column(db.Text)
    password = db.Column(db.Text)

结果:数据库已创建,但它只是一个空文件,里面没有 tables。 我还尝试将 db.create_all() 放在服务 class def __init__(self, db) 中(在这里抓紧稻草),既作为自我参考又作为参数参考。都没有用。

我确信这是我明显遗漏的东西,但我已经将我的项目归结为最低限度,但仍然看不出为什么它不起作用 - 所以我不得不问。如何让 db.create_all() 正确检测我的 table classes 并实际创建所需的 tables,同时使用此代码结构(或类似的东西,以防我是否误解了 MVC)?

问题是server.py执行了两次

  • models/users.py
  • 中导入时
  • server.py 被调用到 运行 应用时

每次执行都会生成一个新的 db 实例。模型文件导入的 db 将模型添加到其 metadata,应用程序 运行 时创建的 db 元数据为空。

您可以通过在 models/users.py 末尾和 main 函数中调用 db.create_all() 之前打印 id(db)db.metadata.tables 来确认这一点.

您需要构建代码以便只创建一个 db。例如,您可以将应用程序配置和创建代码移动到它自己的模块中,mkapp.py(随意想出一个更好的名称):

mkapp.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.sqlite'
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)

并在 server.py 中执行

from mkapp import app, db

并在 models/users.py 中执行

from mkapp import db

作为奖励,这还应该删除导入周期。


我用 Flask 不多,所以这个解决方案可能可以改进。例如,让一个函数创建 appdb 并记住结果可能比在顶级模块代码中创建它们更好。