[英]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.Model
或Base
并返回带有正确父级的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我遇到的其他方法是
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 并且不允许清楚地分离模型,使它们独立并适应上下文。
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.