Django_ORM操作 - 查詢
ORM 操作
必知必會13條
<1> all(): 查詢所有結(jié)果 <2> filter(**kwargs): 它包含了與所給篩選條件相匹配的對象 <3> get(**kwargs): 返回與所給篩選條件相匹配的對象 返回結(jié)果有且只有一個,如果符合篩選條件的對象超過一個或者沒有都會拋出錯誤。 <4> exclude(**kwargs): 它包含了與所給篩選條件不匹配的對象 <5> values(*field): 返回一個ValueQuerySet, 運行后得到的不是一系列model的實例化對象,而是一個可迭代的字典序列 <6> values_list(*field): 它與values()非常相似,它返回的是一個元組序列,values返回的是一個字典序列 <7> order_by(*field): 對查詢結(jié)果排序 <8> reverse(): 對查詢結(jié)果反向排序,請注意reverse()通常只能在具有已定義順序的QuerySet上調(diào)用 (在model類的Meta中指定ordering或調(diào)用order_by()方法)。 <9> distinct(): 從返回結(jié)果中剔除重復(fù)紀(jì)錄 (如果你查詢跨越多個表,可能在計算QuerySet時得到重復(fù)的結(jié)果此時可以使用distinct(), 注意只有在PostgreSQL中支持按字段去重。) <10> count(): 返回數(shù)據(jù)庫中匹配查詢(QuerySet)的對象數(shù)量。 <11> first(): 返回第一條記錄 .all().first 等效于 .first() <12> last(): 返回最后一條記錄 <13> exists(): 如果QuerySet包含數(shù)據(jù),就返回True,否則返回False
返回QuerySet對象的方法有
all() filter() exclude() order_by() reverse() distinct()
特殊的QuerySet
values() 返回一個可迭代的字典序列 values_list() 返回一個可迭代的元祖序列
具體的對象是無法使用這兩個方法的 原理上來說models.py 里面的 class類 中就沒有 這兩個屬性 這兩個屬性只針對于一個QuerySet序列集進(jìn)行篩選才可以使用 比如 .filter(id=1) 雖然只返回了一個QuerySet對象 但是也可以使用
返回具體對象的
get() first() last()
對象可以直接 .屬性 的方法去取值 原理上來說在數(shù)據(jù)庫對象的里面就有屬性自然是可以知己調(diào)用的
返回布爾值的方法
exists()
返回數(shù)字的方法有
count()
對象和QuerySet對象的區(qū)別
具體對象
- 可以直接 .屬性 的方法去取值
- 本質(zhì)上來說具體對象就是 models.py 里面的 class類的實例化,本身就有屬性可以自己調(diào)用
- 無法使用values()和values_list()的, 因為自己的屬性里面就沒有
- 沒有 .update() 方法, 在QuerySet對象才可以調(diào)用
QuerySet對象
- 可以調(diào)用values()和values_list()
- 這兩個屬性只針對于一個QuerySet序列集進(jìn)行篩選才可以使用
- 比如 .filter(id=1) 雖然只返回了一個QuerySet對象 但是也可以使用
轉(zhuǎn)換
QuerySet對象------>具體對象
QuerySet對象.first()
QuerySet對象[0]
# 查詢 "部門表" 的全部內(nèi)容 # 查詢的時候不帶 values或者values_list 默認(rèn)就是查詢 all() ret = models.Employee.objects.all() # """ # SELECT `employee`.`id`, `employee`.`name`, `employee`.`age`, `employee`.`salary`, `employee`.`province`, `employee`.`dept` FROM `employee` LIMIT 21; args=() # """ # 查詢所有人的 "部門" 和 "年齡" # values 或者 values_list 里面寫什么就相當(dāng)于 select 什么字段 ret = models.Employee.objects.all().values("dept", "age") # """ # SELECT `employee`.`dept`, `employee`.`age` FROM `employee` LIMIT 21; args=() # """
單表查詢之神奇的雙下劃線
models.Tb1.objects.filter(id__lt=10, id__gt=1) # 獲取id大于1 且 小于10的值 models.Tb1.objects.filter(id__in=[11, 22, 33]) # 獲取id等于11、22、33的數(shù)據(jù) models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in models.Tb1.objects.filter(name__contains="ven") # 獲取name字段包含"ven"的 models.Tb1.objects.filter(name__icontains="ven") # icontains大小寫不敏感 models.Tb1.objects.filter(id__range=[1, 3]) # id范圍是1到3的,等價于SQL的bettwen and 左右都包含 # 類似的還有:startswith,istartswith, endswith, iendswith # date字段還可以單獨將年月日拿出來 models.Class.objects.filter(birtday__year=2017) models.Class.objects.filter(birtday__month=7) models.Class.objects.filter(birtday__day=17)
基礎(chǔ)查詢操作
基于對象關(guān)聯(lián)查詢
一對多查詢(Book--Publish)
正向查詢,按字段
book_obj.publish : 與這本書關(guān)聯(lián)的出版社對象
book_obj.publish.addr: 與這本書關(guān)聯(lián)的出版社的地址
反向查詢,按表名_set
publish_obj.book_set: 與這個出版社關(guān)聯(lián)的書籍對象集合
publish_obj.book_set.all() :[obj1,obj2,....]
一對一查詢(Author---AuthorDetail)
正向查詢,按字段
author_obj.ad : 與這個作者關(guān)聯(lián)的作者詳細(xì)信息對象
反向查詢:按表名
author_detail_obj.author : 與這個作者詳細(xì)對象關(guān)聯(lián)的作者對象
多對多(Book----Author)
正向查詢,按字段
book_obj.authorList.all(): 與這本書關(guān)聯(lián)的所有這作者對象的集合 [obj1,obj2,....] book_obj.authorList.all().values("name"): 如果想查單個值的時候可以這樣查
反向查詢,按表名_set
author_obj.book_set.all() : 與這個作者關(guān)聯(lián)的所有書籍對象的集合 book_obj.book_set.all().values("name"): 如果想查單個值的時候可以這樣查
基于雙下滑線的跨表查詢(queryset對象查詢)
一對多查詢(Book--Publish)
正向查詢,按字段
# 查詢linux這本書的出版社的名字: models.Book.objects.all().filter(title="linux").values("publish__name")
反向查詢:按表名
# 查詢?nèi)嗣癯霭嫔绯霭孢^的所有書籍的名字 models.Publish.objects.filter(name="人民出版社出版").values("book__title")
一對一查詢(Author---AuthorDetail)
正向查詢,按字段
#查詢egon的手機(jī)號 models.Author.objects.filter(name="egon").values("ad__tel")
反向查詢:按表名
#查詢手機(jī)號是151的作者 models.AuthorDetail.objects.filter(tel="151").values("author__name")
多對多(Book----Author)
正向查詢,按字段
#查詢python這本書的作者的名字 models.Book.objects.filter(title="python").values("authorList__name") [{},{},{},{}]
反向查詢,按表名
#查詢alex出版過的出的價格 models.Author.objects.filter(name="alex").values("book__price")
ps:
如果喲有設(shè)置,反向查詢的時候都用:related_name 的值
publish=models.ForeignKey("Publish",related_name="bookList") authorlist=models.ManyToManyField("Author",related_name="bookList") ad=models.models.OneToOneField("AuthorDetail",related_name="authorInfo")
ManyToManyField
概念原理
利用 關(guān)聯(lián)管理器 進(jìn)行維護(hù)
-
- 外鍵關(guān)系的反向查詢
- 多對多關(guān)聯(lián)關(guān)系
create()
創(chuàng)建一個新的對象,保存對象,并將它添加到關(guān)聯(lián)對象集之中,返回新創(chuàng)建的對象。
models.Author.objects.first().book_set.create(title="羊駝之歌", publish_id=2)
add()
把指定的model對象添加到關(guān)聯(lián)對象集中。
添加對象 >>> author_objs = models.Author.objects.filter(id__lt=3) >>> models.Book.objects.first().authors.add(*author_objs)
添加id >>> models.Book.objects.first().authors.add(*[1, 2])
set()
更新model對象的關(guān)聯(lián)對象。
book_obj = models.Book.objects.first() book_obj.authors.set([2, 3])
remove()
從關(guān)聯(lián)對象集中移除執(zhí)行的model對象
book_obj = models.Book.objects.first() author_obj.books.remove(book_obj) author_obj.books.remove(8) # 把id = 8 的書刪掉
clear()
從關(guān)聯(lián)對象移除一切對象。
book_obj = models.Book.objects.first() book_obj.authors.clear()
注意:
對于ForeignKey對象,clear()和remove()方法僅在null=True時存在。
# ForeignKey字段沒設(shè)置null=True時, class Book(models.Model): title = models.CharField(max_length=32) publisher = models.ForeignKey(to=Publisher) # 沒有clear()和remove()方法: >>> models.Publisher.objects.first().book_set.clear() Traceback (most recent call last): File "<input>", line 1, in <module> AttributeError: 'RelatedManager' object has no attribute 'clear' # 當(dāng)ForeignKey字段設(shè)置null=True時, class Book(models.Model): name = models.CharField(max_length=32) publisher = models.ForeignKey(to=Class, null=True) # 此時就有clear()和remove()方法: dels.Publisher.objects.first().book_set.clear()
注意
對于所有類型的關(guān)聯(lián)字段,add()、create()、remove() 和 clear(), set() 都會馬上更新數(shù)據(jù)庫。
換句話說,在關(guān)聯(lián)的任何一端,都不需要再調(diào)用save()方法。
基于對象以及 queryset 對象的綜合查詢示例
1 from django.shortcuts import render,HttpResponse 2 3 # Create your views here. 4 5 6 from app01 import models 7 8 def query(request): 9 10 # #####################基于對象查詢(子查詢)############################## 11 # 按字段(publish) 12 # 一對多 book -----------------> publish 13 # <---------------- 14 # book_set.all() 15 16 # 正向查詢按字段: 17 18 # 查詢python這本書籍的出版社的郵箱 19 20 # python=models.Book.objects.filter(title="python").first() 21 # print(python.publish.email) 22 23 24 # 反向查詢按 表名小寫_set.all() 25 26 # 蘋果出版社出版的書籍名稱 27 28 # publish_obj=models.Publish.objects.filter(name="蘋果出版社").first() 29 # for obj in publish_obj.book_set.all(): 30 # print(obj.title) 31 32 # 按字段(authors.all()) 33 # 多對多 book -----------------------> author 34 # <---------------- 35 # book_set.all() 36 37 38 # 查詢python作者的年齡 39 # python = models.Book.objects.filter(title="python").first() 40 # for author in python.authors.all(): 41 # print(author.name ,author.age) 42 43 # 查詢alex出版過的書籍名稱 44 45 # alex=models.Author.objects.filter(name="alex").first() 46 # for book in alex.book_set.all(): 47 # print(book.title) 48 49 # 按字段 authorDetail 50 # 一對一 author -----------------------> authordetail 51 # <---------------- 52 # 按表名 author 53 54 55 #查詢alex的手機(jī)號 56 # alex=models.Author.objects.filter(name='alex').first() 57 # print(alex.authorDetail.telephone) 58 59 60 # 查詢家在山東的作者名字 61 62 # ad_list=models.AuthorDetail.objects.filter(addr="shandong") 63 # 64 # for ad in ad_list: 65 # print(ad.author.name) 66 67 68 69 ''' 70 對應(yīng)sql: 71 72 select publish_id from Book where title="python" 73 select email from Publish where nid = 1 74 75 76 ''' 77 78 79 80 81 # #####################基于queryset和__查詢(join查詢)############################ 82 83 # 正向查詢:按字段 反向查詢:表名小寫 84 85 86 # 查詢python這本書籍的出版社的郵箱 87 # ret=models.Book.objects.filter(title="python").values("publish__email") 88 # print(ret.query) 89 90 ''' 91 select publish.email from Book 92 left join Publish on book.publish_id=publish.nid 93 where book.title="python" 94 ''' 95 96 # 蘋果出版社出版的書籍名稱 97 # 方式1: 98 ret1=models.Publish.objects.filter(name="蘋果出版社").values("book__title") 99 print("111111111====>",ret1.query) 100 #方式2: 101 ret2=models.Book.objects.filter(publish__name="蘋果出版社").values("title") 102 print("2222222222====>", ret2.query) 103 104 #查詢alex的手機(jī)號 105 # 方式1: 106 ret=models.Author.objects.filter(name="alex").values("authorDetail__telephone") 107 108 # 方式2: 109 models.AuthorDetail.objects.filter(author__name="alex").values("telephone") 110 111 # 查詢手機(jī)號以151開頭的作者出版過的書籍名稱以及書籍對應(yīng)的出版社名稱 112 113 ret=models.Book.objects.filter(authors__authorDetail__telephone__startswith="151").values('title',"publish__name") 114 print(ret.query) 115 116 117 return HttpResponse("OK")
queryset對象特性及優(yōu)化
特性:
惰性執(zhí)行
books = BookInfo.objects.all() # 此時,數(shù)據(jù)庫并不會進(jìn)行實際查詢 # 只有當(dāng)真正使用時,如遍歷的時候,才會真正去數(shù)據(jù)庫進(jìn)行查詢 for b in books: print(b)
緩存
# 進(jìn)行數(shù)據(jù)庫實際查詢遍歷,保存結(jié)果到bs,會進(jìn)行數(shù)據(jù)庫實際交互 bs = [b.id for b in BookInfo.objects.all()] # 再次調(diào)用緩存結(jié)果bs,不再進(jìn)行數(shù)據(jù)庫查詢,而是使用緩存結(jié)果
優(yōu)化方法:
exists()
簡單的使用if語句進(jìn)行判斷也會完全執(zhí)行整個queryset并且把數(shù)據(jù)放入cache,雖然你并不需要這些數(shù)據(jù)!為了避免這個,可以用exists()方法來檢查是否有數(shù)據(jù):
if queryResult.exists(): #SELECT (1) AS "a" FROM "blog_article" LIMIT 1; args=() print("exists...")
iterator()
當(dāng)queryset非常巨大時,cache會成為問題。
處理成千上萬的記錄時,巨大的queryset可能會鎖住系統(tǒng)進(jìn)程,讓你的程序瀕臨崩潰。
要避免在遍歷數(shù)據(jù)的同時產(chǎn)生queryset cache,可以使用 iterator() 方法 來獲取數(shù)據(jù),處理完數(shù)據(jù)就將其丟棄
objs = Book.objects.all().iterator() # iterator()可以一次只從數(shù)據(jù)庫獲取少量數(shù)據(jù),這樣可以節(jié)省內(nèi)存 for obj in objs: print(obj.title) # 基于迭代器的特性,被遍歷到底部之后下次在使用是無效的了。 for obj in objs: print(obj.title)
使用 .iterator() 來防止生成cache,意味著遍歷同一個queryset時會重復(fù)執(zhí)行查詢.
所以使 用 .iterator() 時需確保操作一個大queryset時沒有重復(fù)執(zhí)行查詢.
總結(jié):
Queryset的cache是用于減少程序?qū)?shù)據(jù)庫的查詢,在通常的使用下會保證只有在需要的時候才會查詢數(shù)據(jù)庫。
使用exists()和iterator()方法可以優(yōu)化程序?qū)?nèi)存的使用。不過,由于它們并不會生成queryset cache,可能 會造成額外的數(shù)據(jù)庫查詢。
聚合
導(dǎo)入
from django.db.models import Avg, Sum, Max, Min, Count
示例
>>> from django.db.models import Avg, Sum, Max, Min, Count
>>> models.Book.objects.all().aggregate(Avg("price"))
{'price__avg': 13.233333}
# 指定名稱
>>> models.Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 13.233333}
# 多次聚合
>>> models.Book.objects.all().aggregate(Avg("price"), Max("price"), Min("price"))
{'price__avg': 13.233333, 'price__max': Decimal('19.90'), 'price__min': Decimal('9.90')}
注意:
aggregate 返回的是一個字典,而不是queryset 對象
分組
命令
Employee.objects.values("dept").annotate(avg=Avg("salary").values(dept, "avg")
示例
# 每個 “省” 的 “平均工資” , 查詢后的結(jié)果為 “省 : 平均工資 ” # annotate前面是什么就按照什么來分組,annotate后面的字段是被分組后被計算的新增數(shù)據(jù)列, ret = models.Employee.objects.values("province").annotate(a=Avg("salary")).values("province", "a") # """ # SELECT `employee`.`province`, AVG(`employee`.`salary`) AS `a` FROM `employee` GROUP BY `employee`.`province` ORDER BY NULL LIMIT 21; args=() # """
# 統(tǒng)計每一本書的作者個數(shù) book_list = models.Book.objects.all().annotate(author_num=Count("author")) # 統(tǒng)計出每個出版社買的最便宜的書的價格 publisher_list = models.Publisher.objects.annotate(min_price=Min("book__price")) publisher_list = models.Book.objects.values("publisher__name").annotate(min_price=Min("price")) # 統(tǒng)計不止一個作者的圖書 book_list = models.Book.objects.annotate(author_num=Count("author")).filter(author_num__gt=1) # 根據(jù)一本圖書作者數(shù)量的多少對查詢集 QuerySet進(jìn)行排序 book_list = models.Book.objects.annotate(author_num=Count("author")).order_by("author_num") # 查詢各個作者出的書的總價格 author_list = models.author.annotate(sum_price=Sum("book__price")).values("name", "sum_price"))
# ORM連表分組查詢 # 根據(jù) "部門" 計算出 "平均工資" 結(jié)果為顯示為 "部門名字 : 平均工資" 的表 ret = models.Person.objects.values("dept_id").annotate(a=Avg("salary")).values("dept__name", "a") # """ # SELECT `dept`.`name`, AVG(`person`.`salary`) AS `a` FROM `person` INNER JOIN `dept` ON (`person`.`dept_id` = `dept`.`id`) GROUP BY `person`.`dept_id`, `dept`.`name` ORDER BY NULL LIMIT 21; args=() # """
查詢每個部門的員工的員工數(shù)
關(guān)鍵點:
- queryset 對象.anntate()
- anntate 按前面的 select 字段進(jìn)行 group by 分組統(tǒng)計 ,
- anntate() 的返回值依舊是 queryset 對象 , 只是增加了分組統(tǒng)計后的鍵值對
- 即 " 分組規(guī)則 " .anntate(" 連表操作,數(shù)據(jù)處理 ") . " 篩選字段 "
- 分組規(guī)則 :
- 基于queryset 方法 對分組關(guān)鍵字段進(jìn)行篩選
- 即 "根據(jù)"什么
- 轉(zhuǎn)換成 sql 語句 為 group by 后面的部分
- 基于queryset 方法 對分組關(guān)鍵字段進(jìn)行篩選
- 連表操作,數(shù)據(jù)處理:
- 視情況進(jìn)行是否連表,以及新增一個計算出的字段
- 即 " 計算 " 什么
- 視情況進(jìn)行是否連表,以及新增一個計算出的字段
- 篩選字段:
- 基于queryset 方法 對新增字段進(jìn)行篩選 并呈現(xiàn)最終結(jié)果
- 即 "想要" 什么
- 轉(zhuǎn)換成 sql 語句 為 select 后面的部分
- 基于queryset 方法 對新增字段進(jìn)行篩選 并呈現(xiàn)最終結(jié)果
- 分組規(guī)則 :
分組查詢的超級詳細(xì)的解析示例:
查詢每個部門的員工總工總?cè)藬?shù)



F查詢
概念
對于基礎(chǔ)的兩個值得比較可以通過上面的方法實現(xiàn)
但是對于兩個字段的比較則需要用到 F 查詢
示例
# 查詢評論數(shù)大于收藏數(shù)的書籍
from django.db.models import F
models.Book.objects.filter(commnet_num__gt=F('keep_num'))
# Django 支持 F() 對象之間以及 F() 對象和常數(shù)之間的加減乘除和取模的操作。
models.Book.objects.filter(commnet_num__lt=F('keep_num')*2)
# 對整個字段的所有值的操作也可以通過 F 函數(shù)實現(xiàn)
# 比如將每一本書的價格提高30元
models.Book.objects.all().update(price=F("price")+30)
關(guān)于修改 char 字段的操作
# 把所有書名后面加上(第一版)
>>> from django.db.models.functions import Concat
>>> from django.db.models import Value
>>> models.Book.objects.all().update(title=Concat(F("title"), Value("("), Value("第一版"), Value(")")))
Q查詢
概念
當(dāng)使用filter 的時候 ,內(nèi)部多個篩選條件是 and 的關(guān)系
若需求為 or 的關(guān)系需要用到 Q 查詢
示例
# 查詢作者名是羊駝或山羊的 models.Book.objects.filter(Q(authors__name="羊駝")|Q(authors__name="山羊"))
復(fù)雜示例
# 可以組合& 和| 操作符以及使用括號進(jìn)行分組來編寫任意復(fù)雜的Q 對象。同時,Q 對象可以使用~ 操作符取反,這允許組合正常的查詢和取反(NOT) 查詢。
# 查詢作者名字是羊駝之歌并且不是2018年出版的書的書名。 models.Book.objects.filter(Q(author__name="羊駝之歌") & ~Q(publish_date__year=2018)).values_list("title")
注意
當(dāng) and 和 or 同時一起用的時候 , Q 查詢需要放在前面
示例
# 查詢出版年份是2017或2018,書名中帶羊駝的所有書。 models.Book.objects.filter(Q(publish_date__year=2018) | Q(publish_date__year=2017), title__icontains="羊駝")
Q查詢的另一種方法:
此方法比通常使用稍微復(fù)雜一些。本質(zhì)上和 .filter(Q(title="yang")|Q(price=123)) 實現(xiàn)效果相同,
但是 這樣子拆分出來可以實現(xiàn) 不在使用字段對象,而是用字符串來篩選
場景適用:
搜索框獲取當(dāng)前get 請求中的參數(shù)時,參數(shù)為字符串形式,用常規(guī)的Q查詢必須依靠字段對象從而無法實現(xiàn)
q = Q() # 將Q實例化對象單獨拿出來 q.connnection = "or" # 默認(rèn)多條件的關(guān)系是 "and" 通過connection 可以改成其他 q.children.append(("title", "yang")) # 添加查詢字段 q.children.append(("price", 123))
鎖
限制住當(dāng)前查詢結(jié)束后才可以其他的操作.保證數(shù)據(jù)的可靠性
select_for_update(nowait=False, skip_locked=False)
示例
entries = Entry.objects.select_for_update().filter(author=request.user)
執(zhí)行原生的sql 語句
1 # 查詢person表,判斷每個人的工資是否大于2000 2 # 利用子查詢,可以寫入原生的sql語句 3 ret = models.Person.objects.all().extra( 4 select={"gt": "salary > 2000"} 5 ) 6 7 # """ 8 # SELECT (salary > 2000) AS `gt`, `person`.`id`, `person`.`name`, `person`.`salary`, `person`.`dept_id` FROM `person` LIMIT 21; args=() 9 # """ 10 11 for i in ret: 12 print(i.name, i.gt)
1 # 執(zhí)行完全的原生的SQL語句,類似pymql 2 from django.db import connection 3 cursor = connection.cursor() # 獲取光標(biāo),等待執(zhí)行SQL語句 4 cursor.execute("""SELECT * from person where id = %s""", [1]) 5 row = cursor.fetchone() 6 print(row)
本文來自博客園,作者:羊駝之歌,轉(zhuǎn)載請注明原文鏈接:http://www.rzrgm.cn/shijieli/p/10049144.html

浙公網(wǎng)安備 33010602011771號