Подтвердить что ты не робот

SQLAlchemy Отношение "многие ко многим" на одной таблице

У меня есть модель SQLAlchemy, настроенная в моем приложении, которая должна имитировать функциональность "подписчиков" в Twitter, т.е. пользователи имеют отношения "многие ко многим" с каждым из них (как последователи, так и последующие). Таблицы структурированы следующим образом (sa - модуль sqlalchemy):

t_users = sa.Table("users", meta.metadata,
    sa.Column("id", sa.types.Integer, primary_key=True),
    sa.Column("email", sa.types.String(320), unique=True, nullable=False),
    ...etc...
    )

t_follows = sa.Table("follows", meta.metadata,
    sa.Column("id", sa.types.Integer, primary_key=True),
    sa.Column("follower_id", sa.types.Integer, sa.ForeignKey('users.id'), nullable=False),
    sa.Column("followee_id", sa.types.Integer, sa.ForeignKey('users.id'), nullable=False)
    )

Однако я столкнулся с небольшим препятствием, пытаясь использовать orm.mapper для создания этих отношений, поскольку вторичная таблица ссылается на ту же основную таблицу в обоих направлениях. Как я могу сопоставить это отношение с ORM?

4b9b3361

Ответ 1

В этом случае вы должны явно писать условия primaryjoin и secondaryjoin:

mapper(
    User, t_users,
    properties={
        'followers': relation(
            User,
            secondary=t_follows,
            primaryjoin=(t_follows.c.followee_id==t_users.c.id),
            secondaryjoin=(t_follows.c.follower_id==t_users.c.id),
        ),
        'followees': relation(
            User,
            secondary=t_follows,
            primaryjoin=(t_follows.c.follower_id==t_users.c.id),
            secondaryjoin=(t_follows.c.followee_id==t_users.c.id),
        ),
    },
)

Я написал этот пример, чтобы помочь вам лучше понять, что означают параметры primaryjoin и secondaryjoin. Конечно, вы можете сортировать его с помощью backref.

Кстати, вам не нужен столбец id в таблице ниже, вместо этого используйте составной первичный ключ. Фактически, вы должны определить уникальное ограничение пары follower_id и followee_id в любом случае (либо как основной, либо дополнительный уникальный ключ).

Ответ 2

Вы также можете сделать это декларативно.

Вот аналогичный пример, основанный на вышеприведенном коде, я использую backref.

VolumeRelationship = Table(
    'VolumeRelationship', Base.metadata,
    Column('ParentID', Integer, ForeignKey('Volumes.ID')),
    Column('VolumeID', Integer, ForeignKey('Volumes.ID'))
    )

class Volume(Base):
    """ Volume Object """
    __tablename__ = "Volumes"

    id = Column('ID', Integer, primary_key=True, nullable=False)
    type = Column('Type', String(25))
    name = Column('Name', String(25))
    poolid = Column('pool', Integer, ForeignKey('Pools.ID'))
    parents = relation(
                    'Volume',secondary=VolumeRelationship,
                    primaryjoin=VolumeRelationship.c.VolumeID==id,
                    secondaryjoin=VolumeRelationship.c.ParentID==id,
                    backref="children")