一、鏈式調用接口
1、all()
使用頻率比較高,相當於SELECT * FROM table 語句,用於查詢所有數據。
Model.objects.all()
2、filter()
使用頻率比較高,根據條件過濾數據,常用的條件基本上字段等於、不等於、大於、小於。當然,還有其他的,比如能修改成產生LIKE查詢的。
Model.objects.filter(content__contains="條件")
3、exclude()
與filter是相反的邏輯
Model.objects.exclude(content__contains="條件")
4、reverse()
將QuerySet中的結果倒敘排列
Model.objects.exclude(content__contains="條件").reverse()
5、distinct()
用來進行去重查詢,產生SELECT DISTINCT這樣的SQL查詢
Model.objects.exclude(content__contains="條件").distinct()
6、none()
返回空的QuerySet
Model.objects.none()
二、非鏈式調用接口
1、get()
比如Post.objects.get(id=1)用於查詢id爲1的文章:如果存在,則直接返回對應的Post實例;如果不存在,則拋出DoesNotExist異常。所以一般情況下,要使用異常捕獲處理
try:
post = Post.objects.get(id=1)
except Post.DoesNotExist:
#做異常情況處理
2、create()
用來直接創建一個Model對象
post = Post.objects.create(title="一起學習")
3、get_or_create
根據條件查找,如果沒查找到,就調用create創建。
# 原語句
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
except Person.DoesNotExist:
obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
obj.save()
# 用get_or_create改寫後
obj, created = Person.objects.get_or_create(first_name='John', last_name='Lennon',
defaults={'birthday': date(1940, 10, 9)})
模型的字段數量較大的話,這種模式就變的非常不易用。
你可以通過ManyToManyField 屬性和反向關聯使用get_or_create()。在這種情況下,你應該限制查詢在關聯的上下文內部。如果你不一致地使用它,將可能導致完整性問題。
4、update_or_create
與get_or_create相同,只是用來做更新操作。
5、count
用於返回QuerySet有多少條記錄,相當於SELECT COUNT(*) FROM table 。
cnt= Post.objects.create(title="一起學習").count()
6、latest
用於返回最新的一條記錄,但要在Model的Meta中定義:get_latest_by= <用來排序的字段>。
Post.objects.all().latest()
7、earliest
同上,返回最早的一條記錄。
Post.objects.all().earliest()
8、first
從當前QuerySet記錄中獲取第一條。
Post.objects.all().first()
9、last
同上,獲取最後一條。
Post.objects.all().last()
10、exists
返回True或者False,在數據庫層面執行SELECT (1) AS "a" FROM table LIMIT 1的查詢,如果只是需要判斷QuerySet是否有數據,用這個接口是最合適的方式。
不要用count或者len(queryset)這樣的操作來判斷是否存在。相反,如果可以預期接下來會用到QuerySet中的數據,可以考慮使用len(queryset)的方式來做判斷,這樣可以減少一次DB查詢請求。
11、bulk_create
同create,用來批量創建記錄。
querysetlist=[]
for i in resultlist:
querysetlist.append(Account(name=i))
Account.objects.bulk_create(querysetlist)
12、in_ bulk
批量查詢,接收兩個參數id_ list和filed_ name。可以通過Post.objects. in_ bulk([1, 2, 3])查詢出id爲1、2、3的數據,返回結果是字典類型,字典類型的key爲查詢條件。返回結果示例: {1: <Post 實例1>, 2: <Post實例2>,3:<Post實例3>}。
13、update
用來根據條件批量更新記錄
Post.objects.filter(owner__name='123').update(title='測試更新')。
14、delete
同update,這個接口是用來根據條件批量刪除記錄。
Post.objects.filter(owner__name='123').delete()
15、values
當我們明確知道只需要返回某個字段的值,不需要Model實例時,用它,用法如下:
title_list = Post.objects.filter(category_id=1).values('title')
# 對於有關聯關係的
Blog.objects.values('name', 'entry__headline')
[{'name': 'My blog', 'entry__headline': 'An entry'},
{'name': 'My blog', 'entry__headline': 'Another entry'}, ...]
返回的結果包含dict的QuerySet,類似這樣: <QuerySet [{'title' :xxx},]>
16、values_list
同values, 但是直接返回的是包含tuple的QuerySet:
titles_list = Post.objects.filter(category=1).values_list('title')
返回結果類似: <QuerySet[("標題",)]>
如果只是一個字段的話,可以通過增加flat=True參數,返回結果都是單獨的值,而不是元組。
title_list = Post.objects.filter(category=1).values_list('title',flat=True)
for title in title__list:
print(title)
17、order_by(*fields)
Entry.objects.
filter
(pub_date__year
=
2005
).order_by(
'-pub_date'
,
'headline'
)
要根據其他 model 字段排序,所用語法和跨關係查詢的語法相同。就是說,用兩個連續的下劃線(__)連接關聯 model 和 要排序的字段名稱, 而且可以一直延伸。例如:
1 |
|
18、dates(field, kind, order='ASC')
返回一個 DateQuerySet ,就是提取 QuerySet 查詢中所包含的日期,將其組成一個新的 datetime.date 對象的列表。
19、datetimes(field, kind, order='ASC')
返回一個 DateTimeQuerySet ,就是提取 QuerySet 查詢中所包含的日期,將其組成一個新的 datetime.datetime 對象的列表。
三、進階接口
除了上面介紹的常用接口外,還有其他用來提高性能的接口,在下面介紹。 在優化Django項目時,尤其要考慮這幾種接口的用法。
1、defer
把不需要展示的字段做延遲加載。比如說,需要獲取到文章中除正文外的其他字段,就可以通過posts = Post.objects.all() .defer('content'),這樣拿到的記錄中就不會包含content部分。但是當我們需要用到這個字段時,在使用時會去加載。代碼:
posts = Post.objects.all().defer('content')
for post in posts: #此時會執行數據庫查詢
print (post.content) #此時會執行數據查詢,獲取到content
當不想加載某個過大的字段時(如text類型的字段),會使用defer,但是上面的演示代產生N+1的查詢問題,在實際使用時千萬要注意!
注意:上面的代碼是個不太典型的 N+1查詢的問題, 一般情況下 由外鍵查詢產生的N+1問題比較多,即一條查詢請求返回N條數據,當我們操作數據時,又會產生額外的請求。這就是N+1問題,所有的ORM框架都存在這樣的問題。
2、only
同defer接口剛好相反, 如果只想獲取到所有的title記錄,就可以使用only,只獲取title的內容,其他值在獲取時會產生額外的查詢。
3、select_related
這就是用來解決外鍵產生的N+1問題的方案。我們先來看看什麼情況下會產生這個問題:
posts = Post.objects.all ()
for post in posts: #產生數據庫查詢
print (post.owner) #產生額外的數據庫查詢
代碼同上面類似,只是這裏用的是owenr(是關聯表)。它的解決方法就是用select_ related接口:
post = Post.objects.all() .select_related('category')
for post in posts: # 產生數據庫查詢,category數據也會一次性查詢出來
print (post.category)
當然,這個接口只能用來解決一對多的關聯關係。對於多對多的關係,還得使用下面的接口。
4、prefetch_related
針對多對多關係的數據,可以通過這個接口來避免N+1查詢。比如,post和tag的關係可以通過這種方式來避免:
posts = Post.objects.all().prefetch_related('tag')
for post in posts:#產生兩條查詢語句,分別查詢post和tag
print(post.tag.al1())
5、extra()——實現複雜的where子句
函數原型:extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
四、常用字段查詢
-
exact
精確匹配。如果指定的值是 None,就會翻譯成 SQL 中的 NULL (詳情請查看 isnull )。
例如:
1 2 |
|
等價的 SQL:
1 2 |
|
-
iexact
忽略大小寫的匹配。
例如:
|
|
等價於如下 SQL :
|
|
-
contains
包含,用來進行相似查詢。
-
icontains
同contains,只是忽略大小寫。
-
exact
精確匹配。
-
iexact
同exact,忽略大小寫。
-
__in
指定某個集合,比如Post.objects.filter(id__in=[1, 2, 3]) 相當於SELECT FROM table WHERE IN (1, 2, 3);。
-
gt
大於某個值。比如:Post.objects.filter(id__gt=1)
注意:是__gt
-
gte
大於等於某個值。
-
lt
小於某個值。
-
lte
小於等於某個值。
-
startswith
以某個字符串開頭,與contains類似,只是會產生LIKE '<關鍵詞>%'這樣的SQL。
Entry.objects.filter(headline__startswith="What")
-
istartswith
同startswith, 忽略大小寫。
Entry.objects.filter(headline__istartswith="What")
-
endswith
以某個字符串結尾。
Entry.objects.filter(headline__
endswith="What")
-
iendswith
同endswith,忽略大小寫。
Entry.objects.filter(headline__
iendswith="What")
-
range
範圍查詢,多用於時間範圍,如Post.objects.filter(created_time__range= ('2018-05-01','2018-06-01'))會產生這樣的查詢: SELECT .. . WHERE created_ time BETWEEN '2018-05-01' AND '2018-06-01' ;。
關於日期類的查詢還有很多,比如date、year和month等,具體等需要時查文檔即可。
這裏你需要理解的是,Django之所以提供這麼多的字段查詢,其原因是通過ORM來操作數據庫無法做到像SQL的條件查詢那麼靈活。
因此,這些查詢條件都是用來匹配對應SQL語句的,這意味着,如果你知道某個查詢在SQL中如何實現,可以對應來看Django提供的接口。
-
isnull
-
year,month, day
對日期/時間字段精確匹配年分,年分用四位數字表示。
例如:
1
Entry.objects.
filter
(pub_date__year
=
2005
)
等價於 SQL:
1
SELECT ... WHERE EXTRACT(
'year'
FROM pub_date)
=
'2005'
;
-
search
-
regex
五、進階查詢
除了上面基礎的查詢語句外,Django還提供了其他封裝,來滿足更復雜的查詢,比如 SELECT ... WHERE id = 1 OR id = 2 這樣的查詢,用上面的基礎查詢就無法滿足。
1、F表達式
F表達式常用來執行數據庫層面的計算,從而避免出現競爭狀態。比如需要處理每篇文章的訪問量,假設存在post.pv這樣的字段,當有用戶訪問時,我們對其加1:
post = Post.objects.get(id=1)
post.pv = post.pv+1
post.save()
這在多線程的情況下會出現問題,其執行邏輯是先獲取到當前的pv值,然後將其加1後賦值給post .pv.最後保存。
如果多個線程同時執行了post = Post.objects.get(id=1),那麼每個線程裏的post .pv值都是一樣的, 執行完加1和保存之後,相當於只執行了一個加1,而不是多個。
這時通過F表達式就可以方便地解決這個問題:
from ajango.ab. models import F
post = Post.objects.get(id=1)
post.pv = F('pv') + 1
post.save():
這種方式最終會產生類似這樣的SQL語句: UPDATE table SET pv = pv +1 WHERE ID = 1。 它在數據庫層面執行原子性操作。
2、Q表達式
Q表達式就是用來解決前面提到的那個OR查詢的,可以這麼用:
from django.db.mode1s import Q
Post.objects.filter(Q(id=1) | Q(id=2))
或者進行AND查詢:
Post.objects.filter(Q(id=1) & Q(id=2))
3、Count
用來做聚合查詢,比如想要得到某個分類下有多少篇文章,簡單的做法就是:
category = Category.objects.get(id=1)
posts_count = category.post_set.count()
但是如果想要把這個結果放到category上呢?通過category.post_count可以訪問到:
from django.db.models import Count
categories = Category.objects.annotate(posts_count=Count('post'))
print(categories[0].posts_count)
這相當於給category動態增加了屬性post_count,而這個屬性的值來源於Count('post'),最後可以用int取整。
4、Sum
同Count類似,只是它是用來做合計的。比如想要統計所有數據字段的總和,可以這麼做:
from django.db.models import Sum
Post.objects.all().aggregate(a=Sum('字段'))
#輸出類似結果:{'a':487}爲字典
python中對字典中鍵值對的獲取:
for i in book:
print(i)#鍵的獲取
print(book[i])#值的獲取
上面演示了QuerySet的annotate和aggregate的用法,其中前者用來給QuerySet結果増加屬性,後者只用來直接計算結果,這些聚合表達式都可以與它們結合使用。
除了Count和Sum外,還有Avg、Min和Max等表達式,均用來滿足我們對SQL査洵的需求。
——————————————————————————————————
參考文檔:https://www.cnblogs.com/JetpropelledSnake/p/9276763.html