1. 程式人生 > >Django 各種關係欄位詳解

Django 各種關係欄位詳解

開發十年,就只剩下這套架構體系了! >>>   

參考資料如下

1. ForeignKey

ForeignKey用於多對一關係,直接對應到資料庫外來鍵的概念。使用ForeignKey需要指定引用的目標表,會自動關聯到目標表的主鍵(一般是id欄位)。

例子如下。

from django.db import models

class Child(models.Model):
    parent = models.ForeignKey('Parent', on_delete=models.CASCADE, )
    # ...

class Parent(models.Model):
    # ...
    pass

對比之 sqlalchemy,一行parent=models.ForeignKey(...)包含了 sqlalchemy 中的ForeignKeyrelationship兩部分內容。

1.1 引數:on_delete

on_delete意為當ForeignKey引用的物件被刪除時進行的操作。

有幾個可以考慮的選項。

1.1.1 models.CASCADE

CASCADE意為級聯,on_delete設定為CASCADE時意為執行級聯刪除。依據文件,Django 會模仿 SQL 的ON DELETE CASCADE,對包含了ForeignKey的物件執行刪除。

需要注意的是不會呼叫被級聯刪除物件上的model.delete()

,但是會發送pre_deletepost_delete訊號。

1.1.1.2 models.PROTECT

PROTECT意為保護,on_delete設定為PROTECT意味著要阻止刪除操作發生。刪除關聯的物件時,ForeignKeyon_delete設定為PROTECT會觸發ProtectedError

1.1.1.3 models.SET_NULL

如其名所述,如果這個ForeignKey是 nullable 的,則關聯的物件刪除時將外來鍵設定為 null。

1.1.1.4 models.SET_DEFAULT

如其名所述,如果這個ForeignKey設定了DEFAULT,則關聯的物件刪除時設定這個外來鍵為DEFAULT

值。

1.1.1.5 models.SET

在關聯的物件刪除時,設定為一個指定的值。這個引數可以接受一個可以賦值給這個 ForeignKey 的物件或者一個可呼叫物件。

官方例子如下。

from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models

def get_sentinel_user():
    return get_user_model().objects.get_or_create(username='deleted')[0]

class MyModel(models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET(get_sentinel_user),
    )

1.1.1.6 models.DO_NOTHING

應該不用多說了吧。Django 不會做多餘的事情,但是如果後端的資料庫服務有強制完整性約束,除非你在資料庫一端自己定義了ON DELETE,否則會觸發IntegrityError

1.2 引數:limited_choice_to

強制約束為 django.admin 或者 ModelForm 渲染時提供有限的可選項。

接受引數為dict或者Q物件、返回Q物件的可呼叫物件。

官方例子。

staff_member = models.ForeignKey(
    User,
    on_delete=models.CASCADE,
    limit_choices_to={'is_staff': True},
)

Q 物件是什麼玩意兒這個我搞明白了再說...

1.3 引數:related_name

設定反向關聯的欄位名,和sqlalchemybackref類似。

舉例來說。

class Child(models.Model):
    parent = models.ForeignKey('Parent')

class Parent(models.Model):
    pass

Parent.child_set.all() # 未設定 related_name
Parent.children.all() # 設定 related_name=children

1.4 引數:related_query_name

related_query_name 和 related_name 類似,設定反向引用查詢時條件的字首名。舉例來說。

class Child(models.Model):
    parent = models.ForeignKey('Parent')
    name = models.CharField(max_length=4)

class Parent(models.Model):
    pass

Parent.objects.filter(Child__name='沙雕網友') # 未設定 related_query_name
Parent.objects.filter(myboy__name='沙雕網友') # 設定 related_query_name=myboy

1.5 引數:to_field

得到ForeignKey關聯的模型的欄位,預設是主鍵,如果指定的不是主鍵那麼必須有unique約束才行。

1.6 引數:db_constraint

要不要建立資料庫層級的約束,也就是通過後端資料庫服務確保資料完整性不受破壞。如果設定為 False 那麼訪問不存在的物件時會觸發 DoesNotExists 異常。

1.7 引數:swappable

用於處理“我有一個抽象類模型但是這個模型有一個外來鍵”的情況,典型就是AUTH_USER_MODEL

一般不用改到,這個屬性控制了資料庫遷移時如何處理這個外來鍵關聯的表,總之保持預設值就行了。

這個功能支援了使用自定義的使用者模型替代 django.auth.models.User 之類的玩意兒。

2. OneToOneField

OneToOneField 基本就是一個加了unique約束的ForeignKey。使用上與 ForeignKey 略有不同。

首先是訪問 OneToOneField 時,得到的不是 QuerySet 而是一個物件例項。

# 優生優育政策(
class Parent(models.Model):
    child = OneToOneField('Child')

class Child(models.Model):
    pass

parent.child # => 得到一個 Child 例項

其次是反向引用的名字是模型名字小寫。

child.parent # => 得到一個 Parent 例項

如果指定 related_name 那就和 ForeignKey 一個表現。

3. ManyToManyField

基本和ForeignKey相同。

3.1 和 ForeignKey 相同的引數

  • related_name
  • related_query_name
  • limited_choices_to
  • db_constraint
  • swappable

limited_choices_to 在指定自定義中間表的情況下無效。

3.2 引數:symmetrical

用於處理一個表自己對自己的多對多引用對稱性。

Django 的預設行為是,我是你的朋友,那麼你就是我的朋友。

設定了這個引數則強迫 Django 改變這個行為,允許“被朋友”。

3.3 引數:through

預設情況下,Django 會自行建立中間表,這個引數強制指定中間表。

預設中間表模型裡包含三個欄位。

  • id
  • <containing_model>_id
  • <other_model>_id

如果是自己和自己的多對多關係,則

  • id
  • from_<model>_id
  • to_<model>_id

3.4 引數:through_fields

當自行指定中間表,中間表又包含了多個外來鍵時,指定關聯的外來鍵用。

舉例。

class ModelA(models.Model):
    b = models.ManyToManyField(ModelB, through='ModelC')

class ModelB(models.Model):
    pass

class ModelC(models.Model):
    a=models.ForeignKey('ModelA')
    b=models.ForeignKey('ModelB')
    c=models.ForeignKey('ModelA')

此時在中間表中ac都是對ModelA的外來鍵,產生了歧義,Django 無法自行決定用哪個外來鍵來關聯 AB 兩個表。

這時提供引數。

b = models.ManyToManyField('ModelB', through='ModelC', through_fields=(a, b))

ManyToManyField 關聯兩個表總是不對稱的關係(指我把你當兄弟,你卻想當我爸爸這樣的關係。此時“我”對“你”的“兄弟”關係就是單向的。),這就形成了來源目標的概念。

through_fields 的第一個元素總被認為是來源欄位,第二個元素是目標欄位。

3.5 引數:db_table

指定 Django 建立的中間表的名字,預設根據兩個表表名和 ManyToManyField