0

「関連アイテム」機能を実装する必要があります。つまり、同じテーブルのアイテムを多対多の方法で相互に任意にリンクできるようにする必要があります。ニュースサイトが関連記事を表示する方法に似ています。

また、次のような双方向の関係が必要です。

a = Item()
b = Item()
a.related.append(b)
assert a in b.related # True

さて、SQLレベルでは、これは「標準」の多対多の関係を変更することで解決できると思います。そのため、関連付けが行われるたびに2つのレコードが関連付けテーブルに挿入されるため、(a -> b)および(b - > a) 2 つの別個のレコード。

別の方法として、多対多テーブルの結合条件で関連付けの両側をチェックすることもできます... JOIN assoc ON a.id = assoc.left_id ...... JOIN assoc ON a.id = assoc.left_id OR a.id = assoc.right_id ...

関係が「通常の」多対多の関係と同様に機能するように、SQLAlchemy でこれを構成する方法はありますか?

私が正しい用語を知らないだけである可能性があります-私が思いついたすべて-「自己参照」、「双方向」、「関連付け」-はSQLAlchemyで他の何かを説明するために使用されます。

4

2 に答える 2

1

属性イベントを使用すると、仕事ができます。以下のサンプル コードを参照してください。コードの小さな醜い部分は、無限の再帰を避けるためだけのものです。

class Item(Base):
    __tablename__ =  "item"

    id = Column(Integer, primary_key=True)
    name = Column(String(255), nullable=False)

    # relationships
    related = relationship('Item', 
            secondary = t_links,
            primaryjoin = (id == t_links.c.from_id),
            secondaryjoin = (id == t_links.c.to_id),
    )

_OTHER_SIDE = set()
from sqlalchemy import event
def Item_related_append_listener(target, value, initiator):
    global _OTHER_SIDE
    if not((target, value) in _OTHER_SIDE):
        _OTHER_SIDE.add((value, target))
        if not target in value.related:
            value.related.append(target)
    else:
        _OTHER_SIDE.remove((target, value))

event.listen(Item.related, 'append', Item_related_append_listener)

# ...
a = Item()
b = Item()
a.related.append(b)
assert a in b.related # True
于 2012-06-27T12:53:04.403 に答える
0

完全を期すために、これが最終的なコードです。removeリスナー メソッドは、グローバル変数の使用を避けるために少し異なります。また、イベントのリスナーもあります。

import sqlalchemy as sa

related_items = sa.Table(
    "related_items",
    Base.metadata,
    sa.Column("id", sa.Integer, primary_key=True),
    sa.Column("from_id", sa.ForeignKey("items.id")),
    sa.Column("to_id", sa.ForeignKey("items.id")),
)


class Item(Base):

    __tablename__ = 'items'
    ...
    related = sa.orm.relationship('Item',
            secondary = related_items,
            primaryjoin = (id == related_items.c.from_id),
            secondaryjoin = (id == related_items.c.to_id),
        )


def item_related_append_listener(target, value, initiator):

    if not hasattr(target, "__related_to__"):
        target.__related_to__ = set()

    target.__related_to__.add(value)

    if target not in getattr(value, "__related_to__", set()):
        value.related.append(target)

sa.event.listen(Item.related, 'append', item_related_append_listener)


def item_related_remove_listener(target, value, initiator):
    if target in value.related:
        value.related.remove(target)

sa.event.listen(Item.related, 'remove', item_related_remove_listener)
于 2012-06-28T01:41:37.280 に答える