简体   繁体   English

如何修复我在 vanilla SQLAlchemy 和 Flask-SQLAlchemy 中使用相同模型的方法?

[英]How to fix my approach to use the same models in vanilla SQLAlchemy and Flask-SQLAlchemy?

I came across several approaches on how to use the vanilla SQLAlchemy models in Flask-SQLAlchemy.遇到几种关于如何在 Flask-SQLAlchemy 中使用普通 SQLAlchemy 模型的方法。

It works like a charm to use models that inherit from Base with Flask-SQLAlchemy.使用从Base继承的模型和 Flask-SQLAlchemy 非常有用。

But I really like that convenience stuff...但我真的很喜欢那些方便的东西......

Job.query.all() # Does not work
db.session.query(Job).all() # Works

So I started to work on this and put together some code, but I am stuck and need some help to get this nice and clean.所以我开始研究这个并整理了一些代码,但我被卡住了,需要一些帮助才能让它变得漂亮和干净。

The following block is a general definition that does not inherit from either.以下块是不继承自两者的一般定义。 It is imported and supposed to be used from Flask-SQLAlchemy and vanilla SQLAlchemy at some point.它是从 Flask-SQLAlchemy 和 vanilla SQLAlchemy 导入并应该在某个时候使用的。

class VanillaMachine():

    __tablename__ = 'machine'

    id = Column(Integer, primary_key=True)
    name = Column(String(100))
    status = Column(Integer)

And there is a factory that takes either db.Model or Base and return Machine with the correct parent:并且有一个工厂接受db.ModelBase并返回带有正确父级的Machine

class MachineFactory:

    def __init__(self, *args, **kwargs):
        pass

    def __new__(cls, *args, **kwargs):

        return type('Machine',(object, VanillaMachine, args[0]), VanillaMachine.__dict__.copy())

I am quite sure that there's something off with that code, but I am not sure where.我很确定该代码有问题,但我不确定在哪里。

If I use it like如果我像这样使用它

db = SQLAlchemy()

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()#

Machine1 = MachineFactory(db.Model)
Machine2 = MachineFactory(Base)

there is a error message有错误信息

sqlalchemy.exc.ArgumentError: Column object 'id' already assigned to Table 'machine'

Can help me to get this straight in a nice, reliable way?可以帮助我以一种不错的、可靠的方式解决这个问题吗?

I know that you could just use a function, pass the parent as argument into VanillaMachine and use some if statement, but that would be too straightforward, right?我知道你可以只使用一个函数,将父作为参数传递给VanillaMachine并使用一些if语句,但这太简单了,对吧? :) :)

Edit:编辑:

Other approaches I came across are我遇到的其他方法是

  1. using the Flask context to use Flask-SQLAlchemy models使用 Flask 上下文来使用 Flask-SQLAlchemy 模型

    with app.app_context(): pass or app.app_context().push()

But this is too focused on Flask for me and does not allow to clearly separate the models, make them independent and adjust to the context.但这对我来说太专注于 Flask 并且不允许清楚地分离模型,使它们独立并适应上下文。

  1. supplying an alternative Base class to db = SQLAlchemy(app, model_class=Base) , see here .db = SQLAlchemy(app, model_class=Base)提供替代基类,请参见此处 This might work for me, but I did not evaluate this so far.这可能对我有用,但到目前为止我还没有对此进行评估。

I found a good solution inspired by a Factory pattern and Declarative Mixins as mentioned in the SQLAlchemy docs.我发现了一个很好的解决方案,其灵感来自于 SQLAlchemy 文档中提到的工厂模式声明式混合

For complex multi-level inheritance scenarios a different approach is needed, using @declared_attr.cascading .对于复杂的多级继承场景,需要使用不同的方法,使用@declared_attr.cascading


from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy import Column, Integer, String
from sqlalchemy import MetaData

from sqlalchemy.ext.declarative import declarative_base
from flask_sqlalchemy import SQLAlchemy

SQLALCHEMY_DATABASE_URI = 'sqlite:///' + '/tmp/test_app.db'
engine = create_engine(SQLALCHEMY_DATABASE_URI, echo=True)

# for vanilla
Base = declarative_base()

# for Flask (import from app once initialized)
db = SQLAlchemy()


class MachineMixin:

    __tablename__ = 'machine'
    id = Column(Integer, primary_key=True)
    name = Column(String(100))
    status = Column(Integer)


class ModelFactory:

    @staticmethod
    def create(which_model, which_parent):

        if which_parent == 'flask_sqlalchemy':

            parent = db.Model

        elif which_parent == 'pure_sqlalchemy':

            parent = Base

        # now use type() to interit, fill __dict__ and assign a name
        obj = type(which_model.__name__ + '_' + which_parent,
                    (which_model, parent),
                    {})
        return obj


test_scenario = 'pure_sqlalchemy' # 'flask_sqlalchemy'

Machine = ModelFactory.create(MachineMixin, test_scenario)

if test_scenario == 'flask_sqlalchemy':

    db.metadata.drop_all(bind=engine)
    db.metadata.create_all(bind=engine)

elif test_scenario == 'pure_sqlalchemy':

    Base.metadata.drop_all(bind=engine)
    Base.metadata.create_all(bind=engine)


Session = sessionmaker(bind=engine)
session = Session()
session.add(Machine(name='Bob', status=1))
session.commit()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM