【问题标题】:Using sqlalchemy declarative in the external worker在外部工作人员中使用 sqlalchemy 声明式
【发布时间】:2014-02-27 13:31:52
【问题描述】:

我有一个使用 Flask 和 sqlalchemy 用 python 编写的网络应用程序。这个应用程序在 Heroku 上运行,作为一个工人,我使用 Ironworker 扩展,我在 models 模块中定义了我的所有模型,如下所示:

from app import db

class Player(db.Model):
    __tablename__ = 'players'
    id = db.Column(db.Integer, primary_key=True)
    ....
    type = db.Column(db.String(50))

    def __init__(self, ....):
        ....

    __mapper_args__ = {
        'polymorphic_identity':'player',
        'polymorphic_on':type
    }

class Outfielder(Player):
    __tablename__ = 'outfielders'
    id = db.Column(db.Integer, db.ForeignKey('players.id'), primary_key=True)

    __mapper_args__ = {
        'polymorphic_identity':'outfielder'
    }

class Goalkeeper(Player):
    __tablename__ = 'goalkeepers'
    id = db.Column(db.Integer, db.ForeignKey('players.id'), primary_key=True)
    __mapper_args__ = {
        'polymorphic_identity':'goalkeeper'
    }

(注意这是如何从应用程序导入数据库的) 我可以通过实例化模型在我的应用程序中创建这些模型的实例,如下所示:

db = SQLAlchemy(app)

gk = models.Goalkeeper('John', 'Snow', 1, 1, 3456, 67, 55, 187)
of = models.Outfielder('Gary', 'Lineker', 1, 1, 3999, 77, 78, 176)

db.session.add(gk)
db.session.add(of)
db.session.commit()

现在,我想做的是能够在我的外部工作人员上创建这些模型的实例。唯一阻止我这样做的是对我的应用程序模块的依赖。我不觉得这种依赖是合理的,并且希望找到一种更好的设计方法,能够简单地将我的模型模块导出到 worker 并在那里初始化 sqlalchemy 实例,并创建我的模型实例并将它们完全保存到数据库中我可以在应用程序本身中执行此操作。

请注意,我已经可以像这样从工作人员访问我的数据库:

db = create_engine(db_url)
metadata = MetaData(db)

print "connection to the database established"

players = Table('players', metadata, autoload=True)
s = players.select()

我只是在寻找一种更好的方法来将我的模型结构反映在工人身上。具体来说,我正在考虑我的模型发生变化时的场景,我将不得不分别维护两端的代码。如果我设法重用模型并在两端使用声明性,那么工作人员将不会有任何维护。

【问题讨论】:

  • 可能最好的方法是将模型与 Flask-SQLAlchemy 分开定义,并允许 Flask-SQLAlchemy 使用在单独模块中定义的模型,而不是让 Flask-SQLAlchemy 让您使用内置的db.模型库。不幸的是,Flask-SQLAlchemy 的设计并不容易做到这一点,这个问题有望be addressed in the next version。参见this question

标签: heroku sqlalchemy flask flask-sqlalchemy ironworker


【解决方案1】:

db.Model 是一个 SQLAlchemy declarative_base(),因此您可以像使用普通 SQLAlchemy 一样使用您的模型。

这个例子有效(YourModel 是一个 Flask-SQLAlchemy 模型):

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

import settings
from models import YourModel

engine = create_engine(settings.SQLALCHEMY_DATABASE_URI)
Session = sessionmaker(bind=engine)    
session = Session()

# do something with your session
session.query(YourModel).first()

您必须将dbapp 解耦,因此您可以使用db.init_app 在不触发app 创建的情况下导入它。

当然你在这里要小心。如果您动态加载设置,您可能会使用 import settings 而不是 current_app.config 从错误的位置加载它,但看起来您已经为您的工作人员分配了地址,所以这对您来说不是问题。

另一种选择是创建另一个 Flask 应用程序,而不是您庞大的网络应用程序。

来自 Flask-SQLAlchemy 文档 (http://pythonhosted.org/Flask-SQLAlchemy/api.html#flask.ext.sqlalchemy.SQLAlchemy):

您仍然可以直接使用 sqlalchemy 和 sqlalchemy.orm,但请注意 Flask-SQLAlchemy 自定义只能通过 此 SQLAlchemy 类的实例。查询类默认为 BaseQuery 对于 db.Query、db.Model.query_class 和默认的 query_class db.relationship 和 db.backref。如果您通过这些接口使用 sqlalchemy 和 sqlalchemy.orm 直接,默认查询类将 是 sqlalchemy 的。

编辑:在将 sqlalchemy 会话与 flask-sqlalchemy 模型混合时要记住的另一件事是,flask-sqlalchemy 会话对象实际上是一个子类,并添加了 _model_changes 字典。根据this answer,如果不添加 _model_changes 字段,您将无法提交会话:

def create_session(config):
    engine = create_engine(config['DATABASE_URI'])
    Session = sessionmaker(bind=engine)
    session = Session()
    session._model_changes = {}
    return session 

【讨论】:

  • 像魅力一样工作,谢谢。我允许自己用一些重要信息编辑你的答案,因为路上有一个问题。
  • 我没有测试提交。谢谢你的更新。看起来像 Flask-SQLAlchemy 错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-09
  • 2019-02-22
  • 2018-11-05
  • 2011-10-10
  • 2011-09-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多