Database design 如何处理;假多对多“;炼金术中的关系?

Database design 如何处理;假多对多“;炼金术中的关系?,database-design,sqlalchemy,many-to-many,Database Design,Sqlalchemy,Many To Many,我正在使用SQLAlchemy映射一个数据库,该数据库具有多个“false”多对多关系的情况。我的意思是,假设我有以下对象: class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True) addresses = relationship('Address', secondary='user_address') class Address(Base): __table

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

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和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]
等等


这是处理这一问题的最佳方法还是有其他解决办法?

有一种非常简单的方法,可以通过使用
uselist=False来定义这种关系,就像在关系两侧的定义中所做的那样:

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
    即可
  • 您不应该为
    用户\u地址
    表定义整个映射类,表定义如上所述