簡體   English   中英

Django 模型字段實際上是對相關模型中字段的引用

[英]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@propertyRoom 我可以將它們全部更改為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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM