Django 多表查詢

發表於2016-05-28

多表查詢是模型層的重要功能之一, Django提供了一套基於關聯欄位獨特的解決方案.

ForeignKey

來自Django官方文件的模型示例:

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

class Author(models.Model):
    name = models.CharField(max_length=50)
    email = models.EmailField()

class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    authors = models.ManyToManyField(Author)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    n_comments = models.IntegerField()
    n_pingbacks = models.IntegerField()
    rating = models.IntegerField()

class ForeignKey

ForeignKey欄位接受一個Model類作為引數, 型別與被參照的欄位完全相同:

 blog = models.ForeignKey(Blog)

ForeignKey.to_field

關聯到的關聯物件的欄位名稱。預設地,Django 使用關聯物件的主鍵。

blog = models.ForeignKey(Blog, to_field=Blog.name)

ForeignKey.db_constraint

Django Model的ForeignKey欄位的主要功能是維護一個一對多的關係, 以進行關聯查詢.

只有在db_constraint=True時Django model才會在資料庫上建立外來鍵約束, 在該值為False時不建立約束.

預設db_constraint=True.

ForeignKey.related_name

這個名稱用於讓關聯的物件反查到源物件.

如果你不想讓Django 建立一個反向關聯,請設定related_name 為 '+' 或者以'+' 結尾.

ForeignKey.related_query_nameForeignKey.related_name作為預設值, 兩者功能的具體說明請參見相關文件

使用ForeignKey查詢

前向查詢

若關係模型A包含與模型B關聯的關聯欄位, 模型A的例項可以通過關聯欄位訪問與其關聯的模型B的例項:

>>> e = Entry.objects.get(id=2)
>>> e.blog # Returns the related Blog object.

修改e.blog並呼叫save方法存入資料庫

>>> e.blog = some_blog
>>> e.save()

如果ForeignKey 欄位有null=True 設定(即它允許NULL值),可以分配None來刪除對應的關聯性

>>> e = Entry.objects.get(id=2)
>>> e.blog = None
>>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"

Django提供了一種使用雙下劃線__的查詢語法:

>>> Entry.objects.filter(blog__name='Beatles Blog')

反向查詢

被索引的關係模型可以訪問所有參照它的模型的例項,如Entry.blog作為Blog的外來鍵,預設情況下Blog.entry_set是包含所有參照Blog的Entry示例的查詢集,可以使用查詢集API取出相應的例項。

>>>b = Blog.objects.get(id=1)
>>>b.entry_set.all()

Entry.blog的related_name和related_query_name可以設定該查詢集的名字。

ManyToManyField

來自Django官網的示例:

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)
    person = models.ForeignKey(Person)
    inviter = models.ForeignKey(Person, related_name="membership_invites")
    invite_reason = models.CharField(max_length=64)
    

class ManyToManyField

ManyToManyField.through

Django 會自動建立一個表來管理多對多關係, 若要手動指定關聯表則需要使用through關鍵字引數.

ManyToManyField.through_fields

上文示例中Membership 有兩個外來鍵指向Person (person 和inviter),這使得關聯關係含混不清並讓Django 不知道使用哪一個。

在這種情況下,必須使用through_fields 明確指定Django 應該使用哪些外來鍵

through_fields 接收一個二元組('field1', 'field2'),其中field1 為指向定義ManyToManyField 欄位的模型的外來鍵名稱(本例中為group),field2 為指向目標模型的外來鍵的名稱(本例中為person).

ManyToManyField.db_table

預設情況下,關聯表的名稱使用多對多欄位的名稱和包含這張表的模型的名稱以及Hash值生成,如:memberShip_person_3c1f5

若要想要手動指定表的名稱,可以使用db_table關鍵字引數指定.

others

下列API和ForeignKey中的同名API相同.

  • ManyToManyField.db_constraint

  • ManyToManyField.related_name

  • ManyToManyField.related_query_name

使用ManyToManyField查詢

多對多關係和ForeignKey具有相似的API.

>>>e = Group.objects.get(id=3)
>>>e.members.all() # Returns all members objects for this Group.

反向查詢:

>>>a = Person.objects.get(id=1)
>>>a.group_set.all()

同樣related_name可以設定反向查詢集的名稱。

新增刪除關聯

因為ManyToManyField自動維護關聯表,程式設計師不便於直接訪問.ManyToManyField提供了API用於新增和刪除關聯(即through表中的記錄).

使用一個自動維護through表的模型作為示例:

class User(models.Model):
    user_id = models.IntegerField(primary_key=True)
    
class Flight(models.Model):
    flight_id = models.IntegerField(primary_key=True)
    reserve = models.ManyToManyField(User, related_name='flight_reserve')
    

首先獲得要進行關聯的Flight和User例項:

flights = Flight.objects.filter(flight_id=flight_id)
if flights.count() != 0:    
    flight = flights[0]
users = User.objects.filter(id=user_id)
if users.count() != 0:
    user = users[0]

通過擁有關聯欄位的Flight例項進行新增關聯操作:

flight.reserve.add(user)
flight.save()

刪除操作與這類似:

flight.reserve.remove(user)
flight.save()

參考資料:

django文件-模型欄位-關聯欄位

django文件 - 執行查詢 - 關聯的物件

django文件 - 執行查詢 - 跨關聯關係查詢

相關文章