【问题标题】:SQLAlchemy Polymorphic Relationship with Concrete InheritanceSQLAlchemy 与具体继承的多态关系
【发布时间】:2012-02-10 06:22:54
【问题描述】:

我正在使用带有 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 是一个抽象类,无法映射到真实存在的表。官网的文档有一个例子,但它的基类不是抽象的。现在应该如何设置与抽象模型的多态关系?

【问题讨论】:

    标签: python orm sqlalchemy flask-sqlalchemy concrete-inheritance


    【解决方案1】:

    我已经找到了问题的原因和解决方法。

    根据sqlalchemy官网的文档,抽象类可以是映射类,因为polymorphic_union函数可以创建一个虚表。

    我使用的是声明式模型,不是手动构建映射器,所以不应该手动创建虚拟表pjoin。基类AbstractConcreteBase 有一个方法__delcare_last__ 会创建带有polymorphic_union 函数的pjoin,但它会在事件after_configured 触发时被调用。

    Post中与Entry的关系会在Post类生成后创建,此时after_configured事件还没有被触发,所以__delcare_last__函数还没有创建虚拟表@ 987654333@ 并将其映射到Entry。所以异常“类'models.entry.Entry'未映射。”将被提升。

    现在,我重构Post模型,让它在__delcare_last__函数中创建与Entry的关系,那么它就会成功,因为触发事件和映射的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)
    
        @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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-05
      • 1970-01-01
      • 2012-10-06
      • 1970-01-01
      • 2020-10-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多