Django框架queryset常用操作

一、鏈式調用接口

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

Entry.objects.order_by('blog__name', 'headline')

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的內容,其他值在獲取時會產生額外的查詢。

這就是用來解決外鍵產生的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)

當然,這個接口只能用來解決一對多的關聯關係。對於多對多的關係,還得使用下面的接口。

針對多對多關係的數據,可以通過這個接口來避免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

Entry.objects.get(id__exact=14)

Entry.objects.get(id__exact=None)

等價的 SQL:

1

2

SELECT ... WHERE id = 14;

SELECT ... WHERE id IS NULL;

  • iexact

忽略大小寫的匹配。

例如:

 

 

Blog.objects.get(name__iexact='beatles blog')

Blog.objects.get(name__iexact=None)

等價於如下 SQL :

 

 

SELECT ... WHERE name ILIKE 'beatles blog';

SELECT ... WHERE name IS NULL;

  • 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

https://www.cnblogs.com/JetpropelledSnake/p/9276457.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章