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?
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 class AbstractConcreteBase
has a method __delcare_last__
would create the pjoin
with polymorphic_union
function, but it would be called while the event after_configured
triggering.
The relationship with Entry
in Post
would be created after the Post
class be generated, in this time the event after_configured
have not been triggered, so __delcare_last__
function have not created the virtual table pjoin
and mapped it into Entry
. So the exception "Class 'models.entry.Entry' is not mapped." will be raised.
Now, I refactor the Post
model, let it create the relationship with Entry
in __delcare_last__
function, then it will be success because of the triggered event and the mapped Entry
.
My new implemented class like this:
class Post(db.Model):
"""An Post."""
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With