理解SQLAlchemy的表继承关系(4)--高级应用
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))
以上Entry是一个基类,没有对应的数据库表,TextEntry和PhotoEntry均继承自Entry。
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")
以上是我们一个应用示例,Post有一个entries皇粮指向不同的entry,在上述代码中我们直接使用relationship来跟Entry创建一个一对多的关系,
理想情况应该是post的entries可以包含不同的Entry,如
>>Post.entries.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>]
但上述代码会出错,出错的原因是因为Entry是一个抽象基类,在数据库中并没有创建对应的数据库表,所以不能创建一对多关系。
幸运的是AbstractConcreteBase提供了__delcare_last__
可以用来解决这些问题,代码如下:
class Post(db.Model): id = db.Column(db.Integer, primary_key=True, nullable=False) description = db.Column(db.Unicode(140), nullable=False) @classmethod def __declare_last__(cls): cls.entries = db.relationship(Entry, viewonly=True) def attach_entries(self, entries): """Attach Entries To This Post. Example: >>> post = Post("An Interesting News", "Wow !!!") >>> text_entry = TextEntry(*t_args) >>> photo_entry = PhotoEntry(*p_args) >>> post.attach_entries([text_entry, photo_entry]) >>> len(post.entries) 2 >>> db.session.commit() >>> """ for entry in entries: self.entries.append(entry) entry.post = self db.session.add(entry)