SQLAlchemy 多态关系与具体继承
我正在使用 SQLAlchemy 的具体表继承。在声明式模型类中,我已成功配置它。
我的代码就像:
class Entry(AbstractConcreteBase, db.Model):
"""Base Class of Entry."""
id = db.Column(db.Integer, primary_key=True, nullable=False)
created = db.Column(db.DateTime, nullable=False)
post_id = declared_attr(lambda c: db.Column(db.ForeignKey("post.id")))
post = declared_attr(lambda c: db.relationship("Post", lazy="joined"))
@declared_attr
def __tablename__(cls):
return cls.__name__.lower()
@declared_attr
def __mapper_args__(cls):
# configurate subclasses about concrete table inheritance
return {'polymorphic_identity': cls.__name__,
'concrete': True} if cls.__name__ != "Entry" else {}
class TextEntry(Entry):
"""Text and Article Entry."""
text = db.deferred(db.Column(db.Text, nullable=False))
class PhotoEntry(Entry):
"""Photo Entry."""
path = db.deferred(db.Column(db.String(256), nullable=False))
在 shell 中测试它时工作正常:
>>> from models.entry import Entry
>>>
>>> Entry.query.all()
[<PhotoEntry 'Title' created by tonyseek>,
<PhotoEntry 'TITLE 2' created by tonyseek>,
<PhotoEntry 'Title 3' created by tonyseek>,
<PhotoEntry 'Title 4' created by tonyseek>,
<TextEntry 'Title' created by tonyseek>]
然后我在其他模型中设置关系时遇到麻烦。每个条目都有一个外键 post_id
来加入 Post
模型,但我无法在 Post
中定义反向引用。这是行不通的:
class Post(db.Model):
"""An Post."""
id = db.Column(db.Integer, primary_key=True, nullable=False)
description = db.Column(db.Unicode(140), nullable=False)
entries = db.relationship(Entry, lazy="dynamic")
它提出了一个异常并说:
InvalidRequestError:一个或多个映射器无法初始化 - 无法继续初始化其他映射器。原始异常是:类“models.entry.Entry”未映射。
显然Entry
是一个抽象类,它无法映射到真实存在的表。官网文档有一个例子,但它的基类不是抽象的。现在应该如何设置与抽象模型的多态关系呢?
I am using the concrete table inheritance with SQLAlchemy. In declartive style model class, I have configured it successfully.
My code just like:
class Entry(AbstractConcreteBase, db.Model):
"""Base Class of Entry."""
id = db.Column(db.Integer, primary_key=True, nullable=False)
created = db.Column(db.DateTime, nullable=False)
post_id = declared_attr(lambda c: db.Column(db.ForeignKey("post.id")))
post = declared_attr(lambda c: db.relationship("Post", lazy="joined"))
@declared_attr
def __tablename__(cls):
return cls.__name__.lower()
@declared_attr
def __mapper_args__(cls):
# configurate subclasses about concrete table inheritance
return {'polymorphic_identity': cls.__name__,
'concrete': True} if cls.__name__ != "Entry" else {}
class TextEntry(Entry):
"""Text and Article Entry."""
text = db.deferred(db.Column(db.Text, nullable=False))
class PhotoEntry(Entry):
"""Photo Entry."""
path = db.deferred(db.Column(db.String(256), nullable=False))
It works fine while testing it in the shell:
>>> from models.entry import Entry
>>>
>>> Entry.query.all()
[<PhotoEntry 'Title' created by tonyseek>,
<PhotoEntry 'TITLE 2' created by tonyseek>,
<PhotoEntry 'Title 3' created by tonyseek>,
<PhotoEntry 'Title 4' created by tonyseek>,
<TextEntry 'Title' created by tonyseek>]
Then I fall into trouble while setting the relationship in other models. Each entry has a foreign key post_id
to join Post
model, but I could not define the back reference in Post
. That can't work:
class Post(db.Model):
"""An Post."""
id = db.Column(db.Integer, primary_key=True, nullable=False)
description = db.Column(db.Unicode(140), nullable=False)
entries = db.relationship(Entry, lazy="dynamic")
It raised a Exception and said:
InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Original exception was: Class 'models.entry.Entry' is not mapped.
Obvious the Entry
is a abstract class, which couldn't be mapped to a real exist table. The document in official website has a example but its base class is not abstract. Now how should I do to set the polymorphic relationship with a abstract model?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我已经找到了问题的原因及其解决方案。
根据sqlalchemy官网的文档,抽象类可以是映射类,因为
polymorphic_union
函数可以创建虚拟表。我使用的是声明式样式模型,而不是手动构建映射器,因此不应手动创建虚拟表
pjoin
。基类AbstractConcreteBase
有一个方法__delcare_last__
可以使用polymorphic_union
函数创建pjoin
,但会在 while 时调用事件after_configured
触发。与
Post
中的Entry
的关系会在Post
类生成后创建,此时事件after_configured
尚未被触发,因此__delcare_last__
函数尚未创建虚拟表pjoin
并将其映射到Entry
。因此,异常“类'models.entry.Entry'未映射”。将被提高。现在,我重构了
Post
模型,让它在__delcare_last__
函数中创建与Entry
的关系,然后就会因为触发事件而成功和映射的Entry
。我新实现的类是这样的:
I have found the reason of the problem and its solution.
According to the document of sqlalchemy offical website, the abstract class could be a mapped class, because the
polymorphic_union
function could create a virtual table.I am using the declartive style model, not build mapper by hand, so the virtual table
pjoin
should not be created by hand. The base classAbstractConcreteBase
has a method__delcare_last__
would create thepjoin
withpolymorphic_union
function, but it would be called while the eventafter_configured
triggering.The relationship with
Entry
inPost
would be created after thePost
class be generated, in this time the eventafter_configured
have not been triggered, so__delcare_last__
function have not created the virtual tablepjoin
and mapped it intoEntry
. So the exception "Class 'models.entry.Entry' is not mapped." will be raised.Now, I refactor the
Post
model, let it create the relationship withEntry
in__delcare_last__
function, then it will be success because of the triggered event and the mappedEntry
.My new implemented class like this: