6

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))

シェルでテストしている間は正常に動作します。

>>> 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:1つ以上のマッパーが初期化に失敗しました-他のマッパーの初期化を続行できません。元の例外は次のとおりです。クラス'models.entry.Entry'はマップされていません。

明らかに、Entryこれは抽象クラスであり、実際に存在するテーブルにマップできませんでした。公式ウェブサイトのドキュメントには例がありますが、その基本クラスは抽象的ではありません。では、抽象モデルとのポリモーフィックな関係を設定するにはどうすればよいですか?

4

1 に答える 1

9

私は問題の理由とその解決策を見つけました。

sqlalchemyの公式ウェブサイトのドキュメントによると、polymorphic_union関数が仮想テーブルを作成できるため、抽象クラスはマップされたクラスである可能性があります。

マッパーを手動で作成するのではなく、宣言型スタイルモデルを使用しているため、仮想テーブルpjoinを手動で作成しないでください。基本クラスにはwith関数を作成AbstractConcreteBaseするメソッドがありますが、イベントのトリガー中に呼び出されます。__delcare_last__pjoinpolymorphic_unionafter_configured

Entryinとの関係は、クラスが生成Postされた後に作成されます。この時点ではイベントはトリガーされていないため、関数は仮想テーブルを作成してにマップしていません。したがって、例外「クラス'models.entry.Entry'はマップされません。」発生します。Postafter_configured__delcare_last__pjoinEntry

ここで、モデルをリファクタリングし、関数内でPostとの関係を作成します。トリガーされたイベントとマップされたイベントにより、成功します。Entry__delcare_last__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)
于 2012-02-11T01:54:52.243 に答える