2

継承された(ミックスイン)クラスに「関係」が欲しいのですが。

ただし、継承オブジェクトを作成すると、リレーションシップオブジェクトはNoneになります。付け加えることはできません。

これを解決するにはどうすればよいですか?

これがドキュメントに基づくコードです

from sqlalchemy import Column, Integer, String, DateTime, Boolean, BigInteger, Float
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
class Target(Base):
    __tablename__ = "target"
    id = Column(Integer, primary_key=True)

class RefTargetMixin(object):
    @declared_attr
    def target_id(cls):
        return Column('target_id', ForeignKey('target.id'))

    @declared_attr
    def target(cls):
        return relationship("Target",
            primaryjoin="Target.id==%s.target_id" % cls.__name__
        )

class Foo(RefTargetMixin, Base):
    __tablename__ = 'foo'
    id = Column(Integer, primary_key=True)

print repr(RefTargetMixin.target)
print repr(Foo.target)
print repr(Foo().target)

出力は次のとおりです。

<sqlalchemy.orm.properties.RelationshipProperty object at 0x24e7890>
<sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x24e7690>
None

一般に、リレーションシップオブジェクト(ターゲット)に追加できるはずですが、ここではNoneであるため追加できません。なんで?

4

2 に答える 2

4

値がNoneである理由は、これを多対1の関係として定義したためです。親から子への多対1は、親に外部キーがあることを意味します。これは、1つの子のみを参照できます。クラスの何かがアイテムのコレクションを参照するようにしたい場合はRefTargetMixin、外部キーがリモート側にある必要があります。

したがって、ここでの目標は、RefTargetMixinのサブクラスであるオブジェクトをターゲットの潜在的な親にすることです。このパターンは、ポリモーフィックアソシエーションパターンと呼ばれます。多くのORMツールキットでは、Targetで「ポリモーフィック外部キー」を宣言することでこれを提供するのが一般的ですが、これは関係的には良い習慣ではないため、答えは何らかの方法で複数のテーブルを使用することです。これには、examples /generic_associationフォルダーのSQLAlchemyコアで提供される3つのシナリオがあります。これには、「識別子付きの単一の関連付けテーブル」、「関連付けごとのテーブル」、「関連ごとのテーブル」が含まれます。各パターンは、ここでRefTargetMixinに同一の宣言型パターンを提供しますが、テーブルの構造が変更されます。

たとえば、これは「関連付けごとのテーブル」を使用したモデルです。これは、複数のタイプのRefTargetMixinオブジェクトを一度にクエリする必要がない場合に、最適にスケーリングされる傾向があります(文字通り、例をそのまま使用し、変更しただけです)。名):

from sqlalchemy.ext.declarative import declarative_base, declared_attr
from sqlalchemy import create_engine, Integer, Column, \
                    String, ForeignKey, Table
from sqlalchemy.orm import Session, relationship

class Base(object):
    """Base class which provides automated table name
    and surrogate primary key column.

    """
    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()
    id = Column(Integer, primary_key=True)
Base = declarative_base(cls=Base)

class Target(Base):
    pass

class RefTargetMixin(object):
    @declared_attr
    def targets(cls):
        target_association = Table(
            "%s_targets" % cls.__tablename__,
            cls.metadata,
            Column("target_id", ForeignKey("target.id"),
                                primary_key=True),
            Column("%s_id" % cls.__tablename__,
                                ForeignKey("%s.id" % cls.__tablename__),
                                primary_key=True),
        )
        return relationship(Target, secondary=target_association)

class Customer(RefTargetMixin, Base):
    name = Column(String)

class Supplier(RefTargetMixin, Base):
    company_name = Column(String)

engine = create_engine('sqlite://', echo=True)
Base.metadata.create_all(engine)

session = Session(engine)

session.add_all([
    Customer(
        name='customer 1',
        targets=[
            Target(),
            Target()
        ]
    ),
    Supplier(
        company_name="Ace Hammers",
        targets=[
            Target(),
        ]
    ),
])

session.commit()

for customer in session.query(Customer):
    for target in customer.targets:
        print target
于 2012-07-11T16:55:53.180 に答える
1

これは正常な動作です。Fooには1つのターゲットがあります。Fooオブジェクトを作成するとき、まだTargetがないため、の値はFoo().targetですNone

Fooに複数のターゲットを設定する場合は、Fooではfoo_idなくTargetに配置target_idし、backrefを使用する必要があります。

また、その場合、プライマリ結合を指定する必要はありません。

于 2012-07-09T16:26:51.947 に答える