복붙노트

[PYTHON] 동일한 속성을 참조하는 SQLAlchemy ORM에서 자체 참조 다 대다 관계를 어떻게 성취 할 수 있습니까?

PYTHON

동일한 속성을 참조하는 SQLAlchemy ORM에서 자체 참조 다 대다 관계를 어떻게 성취 할 수 있습니까?

SQLAlchemy에 대한 선언을 사용하여 자체 참조 다 대다 관계를 구현하려고합니다.

관계는 두 사용자 간의 우정을 나타냅니다. 온라인 어떻게 든 역할이 차별화 된 자체 참조 m2m 관계를 만드는 방법 (문서 및 Google 모두에서)을 찾았습니다. 이것은이 m2m 관계에서 UserA가 UserB의 상사라는 것을 의미하므로 '부하 직원'속성에 따라 그를 나열합니다. 같은 방법으로 UserB는 '상위'아래에 UserA를 나열합니다.

이런 식으로 동일한 테이블에 역 참조를 선언 할 수 있기 때문에 문제가되지 않습니다.

subordinates = relationship('User', backref='superiors')

물론 '상위'속성은 클래스 내에서 명시 적이지 않습니다.

어쨌든, 여기 내 문제가있다 : 내가 backref를 부르고있는 것과 동일한 속성으로 역 참조하고 싶다면 어떻게해야 할까? 이렇게 :

friends = relationship('User',
                       secondary=friendship, #this is the table that breaks the m2m
                       primaryjoin=id==friendship.c.friend_a_id,
                       secondaryjoin=id==friendship.c.friend_b_id
                       backref=??????
                       )

A와 친구가되면 관계 역할이 동일하고 B의 친구를 호출하면 A가있는 목록을 가져와야하기 때문에 이는 의미가 있습니다. 이것은 문제가되는 코드입니다.

friendship = Table(
    'friendships', Base.metadata,
    Column('friend_a_id', Integer, ForeignKey('users.id'), primary_key=True),
    Column('friend_b_id', Integer, ForeignKey('users.id'), primary_key=True)
)

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)

    friends = relationship('User',
                           secondary=friendship,
                           primaryjoin=id==friendship.c.friend_a_id,
                           secondaryjoin=id==friendship.c.friend_b_id,
                           #HELP NEEDED HERE
                           )

이 텍스트가 너무 많으면 죄송합니다.이 작업을 할 수있는 한 명시 적으로하고 싶습니다. 웹상에서 이것에 대한 참고 자료를 찾을 수없는 것 같습니다.

해결법

  1. ==============================

    1.오늘 날 메일 링리스트에서 암시 한 UNION 접근 방식이 있습니다.

    오늘 날 메일 링리스트에서 암시 한 UNION 접근 방식이 있습니다.

    from sqlalchemy import Integer, Table, Column, ForeignKey, \
        create_engine, String, select
    from sqlalchemy.orm import Session, relationship
    from sqlalchemy.ext.declarative import declarative_base
    
    Base= declarative_base()
    
    friendship = Table(
        'friendships', Base.metadata,
        Column('friend_a_id', Integer, ForeignKey('users.id'), 
                                            primary_key=True),
        Column('friend_b_id', Integer, ForeignKey('users.id'), 
                                            primary_key=True)
    )
    
    
    class User(Base):
        __tablename__ = 'users'
    
        id = Column(Integer, primary_key=True)
        name = Column(String)
    
        # this relationship is used for persistence
        friends = relationship("User", secondary=friendship, 
                               primaryjoin=id==friendship.c.friend_a_id,
                               secondaryjoin=id==friendship.c.friend_b_id,
        )
    
        def __repr__(self):
            return "User(%r)" % self.name
    
    # this relationship is viewonly and selects across the union of all
    # friends
    friendship_union = select([
                            friendship.c.friend_a_id, 
                            friendship.c.friend_b_id
                            ]).union(
                                select([
                                    friendship.c.friend_b_id, 
                                    friendship.c.friend_a_id]
                                )
                        ).alias()
    User.all_friends = relationship('User',
                           secondary=friendship_union,
                           primaryjoin=User.id==friendship_union.c.friend_a_id,
                           secondaryjoin=User.id==friendship_union.c.friend_b_id,
                           viewonly=True) 
    
    e = create_engine("sqlite://",echo=True)
    Base.metadata.create_all(e)
    s = Session(e)
    
    u1, u2, u3, u4, u5 = User(name='u1'), User(name='u2'), \
                        User(name='u3'), User(name='u4'), User(name='u5')
    
    u1.friends = [u2, u3]
    u4.friends = [u2, u5]
    u3.friends.append(u5)
    s.add_all([u1, u2, u3, u4, u5])
    s.commit()
    
    print u2.all_friends
    print u5.all_friends
    
  2. ==============================

    2.나는이 동일한 문제를 해결할 필요가 있었고 Friend 클래스로 User 클래스를 서브 클래 싱하고 sqlalchemy.orm.exc.FlushError로 실행하면서 자체 참조 다 대 다 관계로 꽤 많은 것을 망쳤다. 결국 자기 참조 다 대다 관계를 만드는 대신 조인 테이블 (또는 보조 테이블)을 사용하여 자체 참조 일대 다 관계를 만들었습니다.

    나는이 동일한 문제를 해결할 필요가 있었고 Friend 클래스로 User 클래스를 서브 클래 싱하고 sqlalchemy.orm.exc.FlushError로 실행하면서 자체 참조 다 대 다 관계로 꽤 많은 것을 망쳤다. 결국 자기 참조 다 대다 관계를 만드는 대신 조인 테이블 (또는 보조 테이블)을 사용하여 자체 참조 일대 다 관계를 만들었습니다.

    자기 참조 객체로 생각하면 일대 다 다 대다 (many to many) 다. 원래 질문에서 역 참조 문제를 해결했습니다.

    당신이 그것을 실제로 보길 원한다면 나는 또한 훌륭한 예제를 가지고있다. 또한 지금은 ipiththon 노트북을 포함하는 github 형식의 요지처럼 보입니다. 산뜻한.

    friendship = Table(
        'friendships', Base.metadata,
        Column('user_id', Integer, ForeignKey('users.id'), index=True),
        Column('friend_id', Integer, ForeignKey('users.id')),
        UniqueConstraint('user_id', 'friend_id', name='unique_friendships'))
    
    
    class User(Base):
        __tablename__ = 'users'
    
        id = Column(Integer, primary_key=True)
        name = Column(String(255))
    
        friends = relationship('User',
                               secondary=friendship,
                               primaryjoin=id==friendship.c.user_id,
                               secondaryjoin=id==friendship.c.friend_id)
    
        def befriend(self, friend):
            if friend not in self.friends:
                self.friends.append(friend)
                friend.friends.append(self)
    
        def unfriend(self, friend):
            if friend in self.friends:
                self.friends.remove(friend)
                friend.friends.remove(self)
    
        def __repr__(self):
            return '<User(name=|%s|)>' % self.name
    
  3. from https://stackoverflow.com/questions/9116924/how-can-i-achieve-a-self-referencing-many-to-many-relationship-on-the-sqlalchemy by cc-by-sa and MIT license