1. 程式人生 > 程式設計 >基於Django OneToOneField和ForeignKey的區別詳解

基於Django OneToOneField和ForeignKey的區別詳解

根據Django官方文件介紹:

A one-to-one relationship. Conceptually,this is similar to a ForeignKey with unique=True,but the “reverse” side of the relation will directly return a single object.

OneToOneField與ForeignKey加上unique=True效果基本一樣,但是用OneToOneField反向關聯會直接返回物件。

相反地,使用ForeignKey,反向關聯後會返回QuerySet。

例子:

from django.db import models

class Engine(models.Model):
 name = models.CharField(max_length=25)

 def __unicode__(self):
 return self.name

class Car(models.Model):
 name = models.CharField(max_length=25)
 engine = models.OneToOneField(Engine)

 def __unicode__(self):
 return self.name

class Engine2(models.Model):
 name = models.CharField(max_length=25)

 def __unicode__(self):
 return self.name

class Car2(models.Model):
 name = models.CharField(max_length=25)
 engine = models.ForeignKey(Engine2,unique=True)

 def __unicode__(self):
 return self.name

在python manage.py shell裡輸入:

>>> from testapp.models import Car,Engine
>>> c = Car.objects.get(name='Audi')
>>> e = Engine.objects.get(name='Diesel')
>>> e.car # OneToOneField的反向關聯屬性如果沒有寫relate_name,則是對方類名的小寫
<Car: Audi>
>>> from testapp.models import Car2,Engine2
>>> c2 = Car2.objects.get(name='Mazda')
>>> e2 = Engine2.objects.get(name='Wankel')
>>> e2.car2_set.all() # OneToOneField的反向關聯屬性如果沒有寫relate_name,則是對方類名的小寫_set
[<Car2: Mazda>]

補充知識:Django ForeignKey,ManyToManyField和OneToOneField的辨析

導引

模型(Models)是對網站所需資訊種類的定義,其包含了網站儲存資料中的重要欄位和資料行為。一般來說,一個模型對於資料庫中的一個表單。

欄位(Fields)是模型的重要和唯一組成部分,他們由類別的屬性值所指定。

Field分類

由官方文件Model field reference | Django Documentation定義:

Field一共分為AutoField、BinaryField、BooleanField、CharField、DateField、DecimalField、EmailField、FileField、FloatField、IntegerField、TextField、TimeField、URLField等類別,豐富的類別選項為資料庫儲存方式提供了完善的支援,而本文主要是針對如下三個關係型欄位(Relationship fields):

關係型欄位 對應關係
ForeignKey 多對一
ManyToManyField 多對多
OneToOneField 一對一

分析

ForeignKey

首先檢視原始碼,在類的開頭有如下引數:

many_to_many = False
many_to_one = True
one_to_many = False
one_to_one = False

由此可見,ForeignKey是many_to_one型別的,即“一對多”,我們引用官方文件給出的示例:

from django.db import models

class Car(models.Model):
 manufacturer = models.ForeignKey(
 'Manufacturer',on_delete=models.CASCADE,)
 # ...

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

由此我們可以看到,Car型別中有manufacturer欄位,其型別是對應Manufacturer類的ForeignKey。我們可以根據生活常識理解這種定義,由於一部汽車對應一個生產商,而一個生產商可以對應許多部汽車,所以兩者具有“一對多”的關係,在此種情況我們使用ForeignKey。

對於每個ForeignKey,我們需要給出關聯的模型和on_delete響應的選項,即

 manufacturer = models.ForeignKey(
 'Manufacturer',)

on_delete函式的作用是在此欄位被刪除的時候做出的響應,其可選項如下:

選項 功能
CASCADE 級聯刪除,此類選項模仿SQL語句ON DELETE CASCADE,再刪除此欄位資訊的時候同時刪除包含ForeignKey欄位的目標(object)
PROTECT 通過django.db.IntegrityError中的ProtectedError來保護此欄位不被刪除,若進行刪除操作則丟擲錯誤
SET_NULL 將ForeignKey置為空,這隻在null選項為True的時候產生作用
SET_DEFAULT 設為預設值(default value),此預設值已預先對ForeignKey設定
SET() 對ForeignKey設定對SET()函式傳遞的數值
DO_NOTHING 不進行任何操作。若資料庫提高了引用完整性,則此種設定會丟擲一個IntegrityError,除非對這一資料欄位手動添加了SQL語句中的ON DELETE欄位

還可以通過設定abstract屬性來定義一個抽象類:

from django.db import models

class AbstractCar(models.Model):
 manufacturer = models.ForeignKey('Manufacturer',on_delete=models.CASCADE)

 class Meta:
 abstract = True

ForeignKey還有如下的引數可以選擇:

引數 功能
limit_choices_to 通過一個限制對欄位資訊的某一可能選項進行約束,可以通過字典,函式或者查詢值來設定
related_name 可以指定關聯的類在本類中的名稱,通過這一引數可以用兩個欄位名引用同一個類,通過這個名稱父類可以取得子類的值,預設為欄位名
related_query_name 用於filter函式過濾和values函式
to_field 關係關聯的相關物件名稱
db_constraint 控制在資料庫中是否應該建立這一欄位的約束
swappable 用於控制這一欄位對於可交換類模型的行為

ManyToManyField

同樣在原始碼中我們可以找到針對ManyToManyField的如下定義:

many_to_many = True
many_to_one = False
one_to_many = False
one_to_one = False

由此可以知道,ManyToManyField是針對“many-to-many”即多對多關係定義的,它需要知道它關聯的類別。

官方文件給出的示例程式碼可以幫助理解:

from django.db import models

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

class Pizza(models.Model):
 # ...
 toppings = models.ManyToManyField(Topping)

在示例程式碼中,Pizza類的toppings欄位由ManyToManyField與Toppings關聯,我們可以由生活常識得出一片披薩上面會有很多種類的佐料,而一種佐料又可以用來製作多種披薩,兩者滿足“多對多”的關係。

ManyToManyField類有兩個經常使用的引數:through和through_fields,通過這兩個引數可以十分方便地建立中間項的關聯,如示例程式碼所示:

from django.db import models

class Person(models.Model):
 name = models.CharField(max_length=50)

class Group(models.Model):
 name = models.CharField(max_length=128)
 members = models.ManyToManyField(
 Person,through='Membership',through_fields=('group','person'),)

class Membership(models.Model):
 group = models.ForeignKey(Group,on_delete=models.CASCADE)
 person = models.ForeignKey(Person,on_delete=models.CASCADE)
 inviter = models.ForeignKey(
 Person,related_name="membership_invites",)
 invite_reason = models.CharField(max_length=64)

在Group類中有ManyToManyField類的欄位members,這一欄位通過through引數與membership聯絡起來,後者表示“成員資格”,即表示“團體”與“個人”之間關係的中間項,而“through_fields”欄位即為中間項連線起來的兩個類名,此處即group和person兩個類。

ManyToManyField還有以下引數可以選擇:

引數 功能
related_name 同ForeignKey,可以指定關聯的類在本類中的名稱
related_query_name 同ForeignKey,應用於filter和values函式
limit_choices_to 同ForeignKey,但如果自己定義瞭如“Membership”之類的中間類,則不會起到作用
symmetrical 對於迭代定義的ManyToManyField,其為這一欄位建立一個單獨的屬性,而是設定symmetrical屬性為True,若期望使用此類迭代關係,可以手動設定其為False
through 如上所示,用於設定中間項的名字,可以自己定義一箇中間項,若不定義的話系統也會分配一箇中間項
through_fields 通過元組來給出中間項關聯的兩個類名,可以檢視上面的示例
db_table 可以通過這一屬性來手動設定儲存這一欄位的資料表名稱,若不設定則預設為欄位的名稱
db_contraint 是否在資料庫中建立約束
swappable 設定是否指向一個可交換的模型

OneToOneField

原始碼中對OneToOneField的設定如下:

many_to_many = False
many_to_one = False
one_to_many = False
one_to_one = True

可知其是針對單對單的關係設定的欄位。在概念上我們可以理解其為設定unique屬性為True的一種型別,區別之處在於它“反向”的數值會返回一個目標值,這對於繼承關係的表達十分有用,例如一下示例程式:

from django.conf import settings
from django.db import models

class MySpecialUser(models.Model):
 user = models.OneToOneField(
 settings.AUTH_USER_MODEL,)
 supervisor = models.OneToOneField(
 settings.AUTH_USER_MODEL,related_name='supervisor_of',)

OneToOneField既包含ForeignKey中的引數,又包含一個額外的引數parent_link,若定義了一個類,其繼承了一個非抽象的類,而設定parent_link這個函式為True,則會將這個類視作繼承的類的父類,而不是一個新的OneToOneField。

以上這篇基於Django OneToOneField和ForeignKey的區別詳解就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。