【问题标题】:How to deal with "false many-to-many" relationships in SQLAlchemy?如何处理 SQLAlchemy 中的“假多对多”关系?
【发布时间】:2015-02-11 12:11:04
【问题描述】:

我正在使用 SQLAlchemy 映射具有多个“假”多对多关系案例的数据库。我的意思是,假设我有以下对象:

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    addresses = relationship('Address', secondary='user_address')

class Address(Base):
    __tablename__ = 'address'
    id = Column(Integer, primary_key=True)
    users = relationship('User', secondary='user_address')

class UserAddressLink(Base):
    __tablename__ = 'user_address'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('user.id'))
    address_id = Column(Integer, ForeignKey('address.id'))

所以,一个简单的多对多关系,对吗?但是有一个问题:它从来没有打算是多对多的。这实际上是一个一对一的关系,有人出于某种原因决定在数据库中设计这样的关系。每个User 只有一个Address,反之亦然。我无法控制数据库设计(事实上,我只是从这个数据库中读取而不是在上面写)所以我无法改变它。

在 SQLAlchemy 上是否有处理此问题的标准方法?它自动假定这是一个多对多关系,并将 User.adresses 和 Address.users 视为列表。

我处理它的方式是创建属性:

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    _addresses = relatioship('Address', secondary='user_address')

    @property
    def address(self):
        return self.addresses[0] if len(self.addresses) > 0 else None

    @address.setter
    def address(self, value):
        self.addresses = [value]

等等。

这是解决此问题的最佳方法还是有其他解决方法?

【问题讨论】:

    标签: database-design sqlalchemy many-to-many


    【解决方案1】:

    有一种非常直接的方法来定义这种关系,方法是使用uselist = False,就像在关系两侧的One-to-One relationship 定义中所做的那样:

    class User(Base):
        __tablename__ = 'user'
        id = Column(Integer, primary_key=True)
        # other columns
        name = Column(String)
    
    
    class Address(Base):
        __tablename__ = 'address'
        id = Column(Integer, primary_key=True)
        # other columns
        name = Column(String)
    
        # relationship(
        user = relationship(
            User,
            secondary='user_address',
            uselist=False,
            backref=backref('address', uselist=False),
        )
    
    user_address = Table(
        'user_address', Base.metadata,
        Column('id', Integer, primary_key=True),
        Column('use_id', Integer, ForeignKey('user.id')),
        Column('address_id', Integer, ForeignKey('address.id')),
    )
    

    然后您可以根据需要使用代码:

    # add some data
    u1 = User(name='JJ', address=Address(name='superstreet'))
    a2 = Address(name='LA')
    a2.user = User(name='John')
    session.add(u1)
    session.add(a2)
    session.commit()
    session.expunge_all()
    
    # get users and preload addresses as well in one query
    q = session.query(User).options(joinedload(User.address))
    for u in q.all():
        print(u)
        print("  {}".format(u.address))
    

    关于您的代码的更多注释:

    • 你不应该定义双方的关系,只需使用backref即可
    • 你不应该为user_address表定义整个映射类,上面的表定义是

    【讨论】:

      猜你喜欢
      • 2021-09-24
      • 2013-11-05
      • 2021-01-28
      • 2020-10-09
      • 2018-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多