Django 各類關係字段詳解

參考資料以下html

1. ForeignKey

ForeignKey用於多對一關係,直接對應到數據庫外鍵的概念。使用ForeignKey須要指定引用的目標表,會自動關聯到目標表的主鍵(通常是id字段)。python

例子以下。sql

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引用的對象被刪除時進行的操做。django

有幾個能夠考慮的選項。後端

1.1.1 models.CASCADE

CASCADE意爲級聯,on_delete設置爲CASCADE時意爲執行級聯刪除。依據文檔,Django 會模仿 SQL 的ON DELETE CASCADE,對包含了ForeignKey的對象執行刪除。app

須要注意的是不會調用被級聯刪除對象上的model.delete(),可是會發送pre_deletepost_delete信號。post

1.1.1.2 models.PROTECT

PROTECT意爲保護,on_delete設置爲PROTECT意味着要阻止刪除操做發生。刪除關聯的對象時,ForeignKeyon_delete設置爲PROTECT會觸發ProtectedErrorcode

1.1.1.3 models.SET_NULL

如其名所述,若是這個ForeignKey是 nullable 的,則關聯的對象刪除時將外鍵設置爲 null。orm

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 的名字決定。