[英]Django model field that's actually a reference to a field in a related model
如果這個問題很難理解,請提前道歉——我不知道如何措辭。 基本上,我試圖在 Django 模型中創建一種“偽字段”,它的工作原理與任何其他 Django 字段完全一樣,只是它實際上是對相關模型上的字段的引用。
舉個例子,假設我經營一家養狗的旅館。 在我的旅館里,我有房間,每個房間都分配給一個顧客,里面有一只狗。
class Customer(Model):
name = models.CharField(max_length=256, null=False)
class Dog(Model):
name = models.CharField(max_length=256, null=False)
customer = model.ForeignKey(Customer, null=True, on_delete=models.CASCADE) # The dog's owner
class Room(Model):
customer = model.ForeignKey(Owner, null=True, on_delete=models.SET_NULL)
dog = model.ForeignKey(Dog, null=True, on_delete=models.SET_NULL)
數據庫中對應的表看起來像這樣
CUSTOMER:
| ID | NAME |
___________________
| 01 | John Smith |
| 02 | Jane Doe |
DOG:
| ID | NAME | CUSTOMER_ID |
____________________________
| 01 | Rover | 01 |
| 02 | Fido | 01 |
| 03 | Spot | 02 |
ROOM:
| ID | DOG_ID | CUSTOMER_ID |
_____________________________
| 01 | 01 | 01 |
| 02 | 03 | 02 |
所以,我的老板注意到我們在 DB 中存儲了冗余數據:rooms 表並不需要有自己的客戶 ID 列:客戶始終是常駐狗的主人,每個房間只包含一只狗,並且每只狗都有一個主人,所以我們總是可以通過去狗桌並查找主人來將客戶分配到房間。 我被要求以一種對其余代碼庫“完全透明”的方式從房間表中刪除客戶 ID 列。
首先,我可以轉換customer
為@property
在客房類,使用自定義的getter:
class Room(Model):
dog = model.ForeignKey(Dog, null=True, on_delete=models.SET_NULL)
@property
def customer(self):
return self.dog.customer
所以現在在代碼中我們做類似c = room.customer
,它繼續工作。 問題是,代碼庫是滿場的Django查詢像room__customer
,當我提出這一切停止工作customer
為@property
的Room
。 我可以將它們全部更改為room__dog__customer
,這很好用,但是更改不會“完全透明”。
我做了一些研究,並嘗試實現自定義管理器並注釋房間的查詢集:
class RoomManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(customer=F('dog__customer'))
當我這樣做時,我可以在查詢中使用room__customer
,但不能使用room__customer__name
,我想是因為F()
返回主鍵值而不是模型實例( https://docs.djangoproject.com/en/2.1/ref/模型/表達式/#using-f-with-annotations )。
所以我的問題是,有沒有一種我不知道的方法可以使這項工作? 有一種方法可以讓 Room 表現得像它與 Customer 有直接的外鍵關系,而沒有customer_id
存儲在 rooms 表中?
我猜你的經理關於“完全透明”的說法只是一種表達“無論你改變什么都不應該搞砸其他業務邏輯”的方式。 換句話說,應用程序應該像今天一樣繼續工作。 我非常懷疑他是否關心你編輯了多少文件(不過,他可能是一個微觀經理,在這種情況下......我很抱歉)。
也就是說,我認為您將room__customer
更改為room__dog__customer
解決方案是最好的room__dog__customer
。 這一變化讓其他 Django 開發人員很清楚發生了什么(你正在處理關系),這是一個相當簡單的變化。 是的,您最終可能不得不接觸許多文件,但是當您處理后端架構時,這就是生活。
但是,此更改需要考慮的一件事是潛在的性能影響。 您可能需要在查詢中引入select_related
調用,以確保正確連接表。 否則,執行room -> dog -> customer
查找可能會變得昂貴(每次查找需要額外的數據庫往返;這確實會在循環中累加)。
祝你好運!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.