2017-09-03 96 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,到目前爲止這對我很有用。模型定義與簡單的公理聯合讓我成爲一個擁有所有地址的客戶。但是,如果我在運行時修改查詢以添加家庭過濾器,則只有一個(家庭)地址的客戶。 :) –