[英]Creating self-referential tables with polymorphism in SQLALchemy
I'm trying to create a db structure in which I have many types of content entities, of which one, a Comment, can be attached to any other. 我正在尝试创建一个数据库结构,其中我有许多类型的内容实体,其中一个,一个注释,可以附加到任何其他实体。
Consider the following: 考虑以下:
from datetime import datetime
from sqlalchemy import create_engine
from sqlalchemy import Column, ForeignKey
from sqlalchemy import Unicode, Integer, DateTime
from sqlalchemy.orm import relation, backref
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Entity(Base):
__tablename__ = 'entities'
id = Column(Integer, primary_key=True)
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
edited_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
type = Column(Unicode(20), nullable=False)
__mapper_args__ = {'polymorphic_on': type}
# <...insert some models based on Entity...>
class Comment(Entity):
__tablename__ = 'comments'
__mapper_args__ = {'polymorphic_identity': u'comment'}
id = Column(None, ForeignKey('entities.id'), primary_key=True)
_idref = relation(Entity, foreign_keys=id, primaryjoin=id == Entity.id)
attached_to_id = Column(Integer, ForeignKey('entities.id'), nullable=False)
#attached_to = relation(Entity, remote_side=[Entity.id])
attached_to = relation(Entity, foreign_keys=attached_to_id,
primaryjoin=attached_to_id == Entity.id,
backref=backref('comments', cascade="all, delete-orphan"))
text = Column(Unicode(255), nullable=False)
engine = create_engine('sqlite://', echo=True)
Base.metadata.bind = engine
Base.metadata.create_all(engine)
This seems about right, except SQLAlchemy doesn't like having two foreign keys pointing to the same parent. 这似乎是正确的,除了SQLAlchemy不喜欢有两个外键指向同一个父。 It says ArgumentError: Can't determine join between 'entities' and 'comments'; tables have more than one foreign key constraint relationship between them. Please specify the 'onclause' of this join explicitly.
它说ArgumentError: Can't determine join between 'entities' and 'comments'; tables have more than one foreign key constraint relationship between them. Please specify the 'onclause' of this join explicitly.
ArgumentError: Can't determine join between 'entities' and 'comments'; tables have more than one foreign key constraint relationship between them. Please specify the 'onclause' of this join explicitly.
How do I specify onclause
? 我如何指定onclause
?
Try to supplement your Comment.__mapper_args__
to: 尝试补充你的Comment.__mapper_args__
到:
__mapper_args__ = {
'polymorphic_identity': 'comment',
'inherit_condition': (id == Entity.id),
}
PS I haven't understand what you need _idref
relationship for? PS我还不明白你需要什么_idref
关系? If you want to use a comment somewhere where Entity
is expected you could just pass the Comment
instance as is. 如果你想在某个预期Entity
位置使用注释,你可以按原样传递Comment
实例。
To supplement @nailxx's answer: Repeating all that boilerplate is tedious if you have many dependent tables. 补充@ nailxx的答案:如果你有许多依赖表,重复所有样板是很乏味的。 Solution: move it all to a metaclass. 解决方案:将其全部移至元类。
class EntityMeta(type(Entity)):
def __init__(cls, name, bases, dct):
ident = dct.get('_identity',None)
if '__abstract__' not in dct:
xid = Column(None, ForeignKey(Entity.id), primary_key=True)
setattr(cls,'id',xid)
setattr(cls,'__mapper_args__', { 'polymorphic_identity': dct['_identity'], 'inherit_condition': (xid == Entity.id,), 'primary_key':(xid,) })
setattr(cls,'__tablename__',name.lower())
super(EntityMeta, cls).__init__(name, bases, dct)
class EntityRef(Entity):
__abstract__ = True
__metaclass__ = EntityMeta
class Comment(EntityRef):
_identity = 'comment'
[...]
Variations, as an exercise for the reader: You can omit the _identity=…
statement and use the class name, as in the setattr(cls,'__tablename__',…)
line. 变体,作为读者的练习:您可以省略_identity=…
语句并使用类名,如setattr(cls,'__tablename__',…)
行。 Or not overwrite an existing __tablename__ or __mapper_args__ attribute. 或者不覆盖现有的__tablename__或__mapper_args__属性。
Be sure to test dct
and not cls
for existence: the latter will find attributes in the parent class, which you obviously do not want at this point. 一定要测试dct
而不是cls
是否存在:后者会在父类中找到属性,这显然是你现在不想要的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.