如何处理“假多对多” SQLAlchemy中的关系? [英] How to deal with "false many-to-many" relatioships in SQLAlchemy?

查看:116
本文介绍了如何处理“假多对多” SQLAlchemy中的关系?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

class Address(Base) b $ b __tablename__ ='address'
id =列(整数,primary_key = True)
users = relationship('User',secondary ='user_address')

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

多关系,对吧?但是有一个抓住:它从来没有意图是多对多的。这实际上是一个一对一的关系,有人决定像这样在数据库中进行设计,无论什么原因。每个用户只有一个地址,反之亦然。我无法控制数据库设计(实际上,我只是从这个数据库中读取,永远不会写它),所以我不能改变这一点。



有没有一种标准的方法来处理这个SQLAlchemy?它自动地假定这是一个多对多的关系,并将User.adresses和Address.users作为列表。



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

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

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

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

等等。



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

解决方案

通过使用 uselist = False 来定义这种关系是非常直接的方法.org / en / rel_0_9 / orm / relationships.html#一对一rel =nofollow>一对一关系关系两端的定义:

  class User(Base):
__tablename__ ='user'
id =列(Integer,primary_key = True)
#其他列
name =列(String)


类地址(Base):
__tablename__ ='地址'
id =列(整数, primary_key = True)
#其他列
名称=列(字符串)

#关系(
用户=关系(
用户,
secondary ='user_address',
uselist = False,
backref = backref('address',uselist = False),


user_address =表(
'user_address',Base.metadata,
Column('id',Integer,primary_key = True),
列('use_id',Integer,ForeignKey('user.id')),
列('address_id',整数,ForeignKey('address.id')),

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

 #添加一些数据
u1 = User(name ='JJ',address = Address(name ='superstreet'))
a2 =地址(name ='LA')
a2.user =用户(name ='John')
session.add(u1)
session.add(a2)
session.commit()
session.expunge_all()

#获取用户和预加载地址也是一个查询
q = session.query(User).options(joinedload(User.address))
在你的q.all()中:
print(u)
print({}。format(u.address))

代码:




  • 您不应该在双方定义关系,只需使用 backref 对于这个

  • ,您不应该为 user_address 表定义整个映射类,表定义如上所述


I'm mapping a database using SQLAlchemy that have multiple cases of "false" many-to-many relationships. What I mean by this is, suppose I have the following objects:

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'))

So, a simple many-to-many relationship, right? But there's one catch: it was never intended to be many-to-many. This is actually a one-to-one relationship that someone decided to design like this in the database for whatever reason. There's only one Address per User and vice-versa. I have no control over database design (in fact, I'm only reading from this database and never writing on it) so I can't change this.

Is there a standard way of dealing with this on SQLAlchemy? It automatically assumes that this is a many-to-many relationships and treat the User.adresses and Address.users as lists.

The way I'm dealing with it is creating properties:

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]

And so on.

Is this the best way to deal with this or is there any other workaround?

解决方案

There is a very straightforward way to define such a relationship by using uselist = False as done in One-to-One relationship definition on both side of the 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')),
)

Then you can use the code as you desire to:

# 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))

Few more notes on your code:

  • you should not define the relationship on both sides, just use backref for this
  • you should not define the whole mapped class for the user_address table, table definition as above is

这篇关于如何处理“假多对多” SQLAlchemy中的关系?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆