玩 Django — Part 6 (查詢資料 — Query model)

Andy Lu
7 min readApr 27, 2018

在第三篇文章中,當時只有利用 Coffee.objects.all() 列出所有的Coffee objects,其實Django有提供許多查詢的方式,將在本篇文章做一個介紹。

假如有一個Model Coffee如下:

class Coffee(models.Model):
name = models.CharField(max_length=60)
price = models.
IntegerField()
country = models.
ForeignKey(Country)
class Country(models.Model):
name = models.CharField(max_length=60)

QuerySet

開始介紹之前,首先須介紹什麼是QuerySet,顧名思義就是一組查詢後的集合。

QuerySet允許你向資料庫作查詢,但是因為QuerySet的查詢是Lazy的,所以只有在需要顯示資料的時候才會直接存取資料庫。

為了避免頻繁的向資料庫查詢,QuerySet除了有Lazy的特性,還包含了Cache的功能,每一組QuerySet都有Cache,以減少資料庫的存取。

另外,QuerySet也包含了串接的功能,也就是可以連續下不同的query,最後只會有一次的資料庫存取。

EX:

>>> Coffee.objects.filter(
... country__name__startswith='Africa'
... ).filter(
... price__gte=100
... )

=> 列出產地名稱開頭為 Africa 且價格高於100的咖啡物件。

回傳QuerySet的方法

all()

all() 是最容易的查詢語法,可以將所有的物件都列出來。

Ex: Coffee.objects.all()

filter(**kwargs) / exclude(**kwargs)

filter(**kwargs)可以根據代入的參數來決定輸出的物件。

反之,exclude(**kwargs)是輸出排除輸入的參數以外的物件。

Ex: Coffee.objects.filter(name='latte')

=> 列出名稱為latte的 物件

Coffee.objects.exclude(name='latte')

=> 列出名稱為非latte的物件

annotate(*args, **kwargs)

假如將原本的Coffee model改寫如下:

class Coffee(models.Model):
name = models.CharField(max_length=60)
price = models.IntegerField()
country = models.ForeignKey(Country)
flavor = models.ManyToManyField(Flavor)
class Country(models.Model):
name = models.CharField(max_length=60)
class Flavor(models.Model):
name = models.CharField(max_length=60)

當需要得知某個Coffee model底下的口味數量可以用annotate。

from django.db.models import Count
c = Coffee.objects.annotate(Count(flavor))
c[0].flavor__count # 顯示幾種口味
--> 5

=> 可以看出在Flavor model上並沒有count的欄位,但是利用annotate就可以將這個欄位暫時的填入目前的QuerySet

order_by()

將搜尋出來的QuerySet利用order_by的順序再次排序。

EX: Coffee.objects.filter(country__name='Africa').order_by('-price', 'name') 將Coffee的Object利用filter搜尋出來之後,再將此QuerySet按照price降冪,name升冪的方式排序

distinct()

當搜尋多個Table時,有可能會出現重複的資料,利用 distinct() 即可以消除重複的項目。

union()

>>> qs1.union(qs2, qs3)

union()是在django 1.11所新增的功能,可以將兩個QuerySet合併起來。

不是回傳QuerySet的方法

get()

利用get()可以直接取得物件,其效果等同於filter()[0]。
當物件不存在時,會拋出例外 DoesNotExist

aggregate(*args, **kwargs)

類似 annotate() ,不過aggregate是回傳一組dict。

Ex:

from django.db.models import Count
c = Coffee.objects.aggregate(Count(flavor))
--> {'flavor__count': 5}

exist()

回傳True, 如果QuerySet存在

限制取得物件的數量

當輸出為QuerySet()時,可以在查詢語句後方加入範圍來限制資料數量。

EX: Coffee.objects.all()[:10]

取得前十個物件。

EX: Coffee.objects.filter(price='100')[2:5]

取得價格為100的第三~第六個物件。

欄位的查詢

有時候,我們並不只是要特定的名稱、價格,我們會需要針對欄位作不同的查詢。

查詢格式:field__lookuptype=value

數字,日期範圍的查詢可以使用:

GT (Greater than): 大於

LT (Less than): 小於

GTE (Greater Than or Equal): 大於或等於

LTE (Less Than or Equal): 小於或等於

名稱的查詢可以使用:

startswith: 大小寫,開頭的字串相符

istartswith: 大小寫不須一樣,開頭的字串相符

endswith: 大小寫,結尾的字串相符

iendswith: 大小寫不須一樣,結尾的字串相符

exact: 精確的比對

iexact: 不精確的比對(大小寫不須相同)

contains: 包含此文字

查詢foreign key的值:

EX:

Coffee.objects.filter(country__name='Africa')

=> 查詢Coffee object中,country name為Africa的Object。

小結

研究django所提供的query model的方法之後,針對不同的使用需求可以用不同的方法,了解之後,就不會只是使用filter(), all(), get()了。

--

--

Andy Lu

Android/Flutter developer, Kotlin Expert, like to learn and share.