Django文檔——Model中的ForeignKey,ManyToManyField與OneToOneField

關聯關係字段 (Relationship fields)

ForeignKey,ManyToManyField與OneToOneField分別在Model中定義多對一,多對多,一對一關係。python

例如,一本書由一家出版社出版,一家出版社能夠出版不少書。一本書由多個做者合寫,一個做者能夠寫不少書。數據庫

class Author(models.Model):
    name=models.CharField(max_length=20)
class Publisher(models.Model):
    name=models.CharField(max_length=20)
class Book(models.Model):
    name=models.CharField(max_length=20)
    pub=models.ForeignKey(Publisher)
    authors=models.ManyToManyField(Author)

1.關聯還沒有定義的Modeldjango

若是你要與某個還沒有定義的 model 創建關聯 ,就使用 model 的名稱,而不是使用 model 對象自己。spa

例子中,若是Publisher與Author在Book後面定義,須要寫成下面的形式:rest

class Book(models.Model):
    name=models.CharField(max_length=20)
    pub=models.ForeignKey('Publisher')
    authors=models.ManyToManyField('Author')

2.Model關聯自身code

Model能夠與自身作多對一關係對象

class People(models.Model):
    name=models.CharField(max_length=20)
    leader=models.ForeignKey('self',blank=True,null=True)

Model也能夠與自身作多對多關係blog

class Person(models.Model):
    friends = models.ManyToManyField("self")

默認狀況下,這種關聯關係是對稱的,若是Person1是Person2的朋友,那麼Person2也是Person1的朋友繼承

p1=Person()
p1.save()
p2=Person()
p2.save()
p3=Person()
p3.save()
p1.friends.add(p2,p3)

上述狀況下,要查找p3的朋友,不用p3.person_set.all(),而直接用p3.friends.all()就能夠了遞歸

若是想取消這種對稱關係,將symmetrical設爲False

class Person2(models.Model):
    friends=(models.ManyToManyField("self",symmetrical=False)

這樣查詢p3的朋友,就須要p3.person_set.all()了

3.反向名稱related_name

反向名稱,用來從被關聯字段指向關聯字段

注意,在你定義 抽象 model (abstract models) 時,你必須顯式指定反向名稱; 只有在你這麼作了以後, 某些特別語法 (some special syntax) 才能正常使用。

class Book(models.Model):
    name=models.CharField(max_length=20)
    pub=models.ForeignKey(Publisher,related_name='pub')
    authors=models.ManyToManyField(Author,related_name='author')

這樣用Publisher或者Author反向查詢Book時能夠用related_name了:publisher1.pub.all()或者author1.author.all()。

若是不想設置反向關係,設置related_name爲'+'或者以'+'結束。

user = models.ForeignKey(User, related_name='+')

若是有多個ManyToManyField指向同一個Model,這樣反向查詢FOO_set的時候就沒法弄清是哪一個ManyToManyField字段了,能夠禁止反向關係:

users = models.ManyToManyField(User, related_name='u+')
referents = models.ManyToManyField(User, related_name='ref+')

4.數據庫表現 (Database Representation)

多對一:Django 使用ForeignKey字段名稱+ "_id" 作爲數據庫中的列名稱。在上面的例子中,BOOK model 對應的數據表中會有 一個 publisher_id 列。

你能夠經過顯式地指定 db_column 來改變該字段的列名稱,不過,除非你想自定 義 SQL ,不然不必更改數據庫的列名稱。

多對多:Django 建立一箇中間表來表示ManyToManyField關係。默認狀況下,中間表的名稱由兩個關係表名結合而成。

因爲某些數據庫對錶名的長度有限制,因此中間表的名稱會自動限制在64個字符之內,幷包含一個不重複的哈希字符串。這 

意味着,你可能看到相似 book_authors_9cdf4 這樣的表名稱。你可使用 db_table 選項手動指定中間表名稱。

可是,若是你想手動指定中間表,你能夠用 through 選項來指定model 使用另外某個 model 來管理多對多關係。而這個 model 就是中間表所對應的 model :

class Person(models.Model):
    name = models.CharField(max_length=128)
    def __unicode__(self):
        return self.name
class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')
    def __unicode__(self):
        return self.name
class Membership(models.Model):
    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

這樣,就能夠記錄某個person什麼時候加入group了。

要創建Person與Group的關係就不能用add,create,remove了,而是須要經過Membership進行。

>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
...     date_joined=date(1962, 8, 16),
...     invite_reason= "Needed a new drummer.")
>>> m1.save()

clear()仍是可使用的

>>> beatles.members.clear()

當多對多關係關聯自身時,中間表的ForeignKey是能夠指向同一個Model的,可是它們必須被看作ManyToManyField的兩邊,而不是對稱的,須要設置 symmetrical=False。

5.其它參數 (Arguments)

5.1 ForeignKey 接受下列這些可選參數,這些參數定義了關係是如何運行的。

ForeignKey.limit_choices_to

它是一個包含篩選條件和對應值的字典,用來在 Django 管理後臺篩選 關聯對象。例如,利用 Python 的 datetime 模塊,過濾掉不符合篩選條件關聯對象:

limit_choices_to = {'pub_date__lte': datetime.date.today}

只有 pub_date 在當前日期以前的關聯對象才容許被選。

也可使用 Q 對象來代替字典,從而實現更復雜的篩選。當limit_choices_to爲Q對象時,若是把此外鍵字段放在ModelAdmin的raw_id_fields時是不可用的。

ForeignKey.to_field

指定當前關係與被關聯對象中的哪一個字段關聯。默認狀況下,to_field 指向被關聯對象的主鍵。

ForeignKey.on_delete

當一個model對象的ForeignKey關聯的對象被刪除時,默認狀況下此對象也會一塊兒被級聯刪除的。

user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)

CASCADE:默認值,model對象會和ForeignKey關聯對象一塊兒被刪除

SET_NULL:將model對象的ForeignKey字段設爲null。固然須要將null設爲True。

SET_DEFAULT:將model對象的ForeignKey字段設爲默認值。

Protect:刪除ForeignKey關聯對象時會生成一個ProtectedError,這樣ForeignKey關聯對象就不會被刪除了。

SET():將model對象的ForeignKey字段設爲傳遞給SET()的值。

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

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

DO_NOTHING:啥也不作。

5.2 ManyToManyField 接受下列可選參數,這些參數定義了關係是如何運行的。

ManyToManyField.limit_choices_to

和 ForeignKey.limit_choices_to 用法同樣。

limit_choices_to 對於經過 through 參數指定了中介表的 ManyToManyField 不起做用。

ManyToManyField.symmetrical

只要定義遞歸的多對多關係時起做用。

ManyToManyField.through

手動指定中間表

ManyToManyField.db_table

指定數據庫中保存多對多關係數據的表名稱。若是沒有提供該選項,Django 就會根據兩個關係表的名稱生成一個新的表名,作爲中間表的名稱。

6.OneToOneField

class OneToOneField(othermodel[, parent_link=False, **options])

用來定義一對一關係。籠統地講,它與聲明瞭 unique=True 的 ForeignKey 很是類似,不一樣的是使用反向關聯的時候,獲得的不是一個對象列表,而是一個單獨的對象。

在某個 model 擴展自另外一個 model 時,這個字段是很是有用的;例如: 多表繼承 (Multi-tableinheritance) 就是經過在子 model 中添加一個指向父 model 的一對一關聯而實現的。

必須給該字段一個參數:被關聯的 model 類。工做方式和 ForeignKey 同樣,連遞歸關聯 (recursive) 和 延後關聯 (lazy) 都同樣。

此外,OneToOneField 接受 ForeignKey 可接受的參數,只有一個參數是 OnetoOneField 專有的:OneToOneField.parent_link

若是爲 True ,而且做用於繼承自某個父 model 的子 model 上(這裏不能是延後繼承,父 model 必須真實存在 ),那麼該字段就會變成指向父類實例的引用(或者叫連接),

而不是象其餘OneToOneField 那樣用於擴展父類並繼承父類屬性。

from django.db import models, transaction, IntegrityError

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

    def __unicode__(self):
        return u"%s the place" % self.name

class Restaurant(models.Model):
    place = models.OneToOneField(Place, primary_key=True)
    serves_hot_dogs = models.BooleanField()
    serves_pizza = models.BooleanField()

    def __unicode__(self):
        return u"%s the restaurant" % self.place.name

class Waiter(models.Model):
    restaurant = models.ForeignKey(Restaurant)
    name = models.CharField(max_length=50)

    def __unicode__(self):
        return u"%s the waiter at %s" % (self.name, self.restaurant)

使用反向關聯的時候,獲得的不是一個對象列表,而是一個單獨的對象:

>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
>>> p1.save()
>>> r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
>>> r.save()
>>> p1.restaurant
<Restaurant: Demon Dogs the restaurant>
>>> Place.objects.get(restaurant__place__name__startswith="Demon")
<Place: Demon Dogs the place>
>>> Waiter.objects.filter(restaurant__place__name__startswith="Demon")
相關文章
相關標籤/搜索