2017-09-03 98 views
0

我在试着了解是否有可能与Sqlalchemy做些什么,或者如果我正在考虑错误的方式。作为一个例子,说我有两个(这些只是例子)类:Python sqlalchemy动态关系

class Customer(db.Model): 
    __tablename__ = 'customer' 
    id = Column(Integer, primary_key=True) 
    name = Column(String) 
    addresses = relationship('Address') 

class Address(db.Model): 
    __tablename__ = 'address' 
    if = Column(Integer, primary_key=True) 
    address = Column(String) 
    home = Column(Boolean) 
    customer_id = Column(Integer, ForeignKey('customer.id')) 

后来我想执行一个查询,得到了客户和只是他们的家庭住址。是否有可能做的是这样:

db.session.query(Customer).join(Address, Address.home == True) 

请问上面进一步细化/限制参加这样的结果只会得到的住址吗?

由于提前, 道格

回答

0

是啊,这是完全可能的,虽然你可能会想要一个像代码:

# if you know the customer's database id... 
# get the first address in the database for the given id that says it's for home 
home_address = db.session.query(Address).filter_by(customer_id=customer_id_here, home=True).first() 

而不必为家庭一个布尔值,你可以尝试一个“类型”行,而不是使用枚举。这可以让你轻松地为工作地点选择地址,而不仅仅是“这个地址是否适用于家庭”的二元选择。

更新:你也可以考虑在关系调用中使用back_populates关键字参数,所以如果你有一个地址实例(叫做a),你可以用a.customer(它是实例与此地址关联的客户类别)。

+0

感谢您的答复。我应该更清楚我的问题,这两个类只是我用来说明问题的例子。示例类建模一对多关系,ForeignKey指定连接。如果它执行简单的查询以获取客户,则地址关系集合也包含该客户的所有地址。但是我想要的是重写或添加联接条件,这样我就可以查询来获得客户,但关系集合只有一个元素 - 家庭地址。 –

1

疑问时如果查询结构是你想要的东西,尝试打印:

In [29]: db.session.query(Customer).join(Address, Address.home == True) 
Out[29]: <sqlalchemy.orm.query.Query at 0x7f14fa651e80> 

In [30]: print(_) 
SELECT customer.id AS customer_id, customer.name AS customer_name 
FROM customer JOIN address ON address.home = true 

很显然,这是不是你想要的。每个客户都加入了每个家庭地址。由于实体的处理方式,起初可能并不明显。即使底层查询错误,每个客户的重复行也会被忽略,并得到不同客户实体的结果。查询还可以在形成结果时有效地忽略加入的地址。

最简单的办法是只查询客户和地址元组要求的标准:

db.session.query(Customer, Address).\ 
    join(Address).\ 
    filter(Address.home) 

你也可以做这样的事情

db.session.query(Customer).\ 
    join(Address, (Customer.id == Address.customer_id) & Address.home).\ 
    options(contains_eager(Customer.addresses)) 

,但我强烈建议反对。你会对自己的关系集合中包含的内容撒谎,这可能会在某些时候适得其反。相反,您应该添加一个新的一对一的关系,客户与custom join condition

class Customer(db.Model): 
    ... 
    home_address = relationship(
     'Address', uselist=False, 
     primaryjoin='and_(Customer.id == Address.customer_id, Address.home)') 

,然后你可以使用一个加入渴望负荷

db.session.query(Customer).options(joinedload(Customer.home_address)) 
+0

@ write-on我会在你的评论中提出你在这里建议的编辑方式:“这是在一个函数调用中,其中home_only是该函数的一个参数,所以我可以得到所有的客户地址或只是在家里。你解释一下你不愿意这么做(因为它接近你的例子)?“ - 我不愿意在关系中实现这种双重角色,因为它可能会让你困扰,尽管现在看起来似乎不太可能。将来,来自该函数调用的对象可能最终会出现在您或其他人希望该集合持有所有地址的地方 –

+0

......并且由于有一种更清晰的方法来处理与单独的“家庭”关系一个关系,通过创建这样一个特殊情况来增加系统的复杂性几乎没有什么收获,在这种情况下,地址集合可能包含所有地址或仅存在于家中。但一如既往,ymmv,如果解决方案适合你,我是谁争论。 –

+0

再次感谢llja,到目前为止这对我很有用。模型定义与简单的公理联合让我成为一个拥有所有地址的客户。但是,如果我在运行时修改查询以添加家庭过滤器,则只有一个(家庭)地址的客户。 :) –