Django_ORM操做 - 查詢

ORM 操做

必知必會13條

<1> all(): 
    查詢全部結果

<2> filter(**kwargs): 
    它包含了與所給篩選條件相匹配的對象

<3> get(**kwargs): 
    返回與所給篩選條件相匹配的對象
    返回結果有且只有一個,若是符合篩選條件的對象超過一個或者沒有都會拋出錯誤。

<4> exclude(**kwargs): 
    它包含了與所給篩選條件不匹配的對象

<5> values(*field): 
    返回一個ValueQuerySet,
    運行後獲得的不是一系列model的實例化對象,而是一個可迭代的字典序列

<6> values_list(*field): 
    它與values()很是類似,它返回的是一個元組序列,values返回的是一個字典序列

<7> order_by(*field): 
    對查詢結果排序

<8> reverse(): 
    對查詢結果反向排序,請注意reverse()一般只能在具備已定義順序的QuerySet上調用
    (在model類的Meta中指定ordering或調用order_by()方法)。

<9> distinct(): 
    從返回結果中剔除重複紀錄
    (若是你查詢跨越多個表,可能在計算QuerySet時獲得重複的結果此時可使用distinct(),
    注意只有在PostgreSQL中支持按字段去重。)

<10> count():  
    返回數據庫中匹配查詢(QuerySet)的對象數量。

<11> first(): 
    返回第一條記錄
    .all().first 等效於 .first() 
 
<12> last(): 
    返回最後一條記錄
  
<13> exists(): 
    若是QuerySet包含數據,就返回True,不然返回False 

返回QuerySet對象的方法有

all()
filter()
exclude()
order_by()
reverse()
distinct()	

特殊的QuerySet

values() 返回一個可迭代的字典序列
values_list() 返回一個可迭代的元祖序列	
  

  具體的對象是沒法使用這兩個方法的   原理上來講models.py 裏面的 class類 中就沒有 這兩個屬性   這兩個屬性只針對於一個QuerySet序列集進行篩選才可使用     好比 .filter(id=1) 雖然只返回了一個QuerySet對象 可是也可使用

返回具體對象的

get()
first()
last()

  對象能夠直接 .屬性 的方法去取值   原理上來講在數據庫對象的裏面就有屬性天然是能夠知己調用的

返回布爾值的方法

exists()

返回數字的方法有

count()

對象和QuerySet對象的區別

具體對象python

  •   能夠直接 .屬性 的方法去取值
  •   本質上來講具體對象就是 models.py 裏面的 class類的實例化,自己就有屬性能夠本身調用
    • 沒法使用values()和values_list()的, 由於本身的屬性裏面就沒有
    • 沒有 .update() 方法, 在QuerySet對象才能夠調用

QuerySet對象linux

  •   能夠調用values()和values_list()
  •   這兩個屬性只針對於一個QuerySet序列集進行篩選才可使用
    • 好比 .filter(id=1) 雖然只返回了一個QuerySet對象 可是也可使用

轉換sql

  QuerySet對象------>具體對象數據庫

    QuerySet對象.first()django

    QuerySet對象[0]緩存

# 查詢 "部門表" 的所有內容
    # 查詢的時候不帶 values或者values_list 默認就是查詢 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 裏面寫什麼就至關於 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等於十一、2二、33的數據

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)

基礎查詢操做

基於對象關聯查詢

一對多查詢(Book--Publish)

正向查詢,按字段app

book_obj.publish : 與這本書關聯的出版社對象 
book_obj.publish.addr: 與這本書關聯的出版社的地址

反向查詢,按表名_setide

publish_obj.book_set: 與這個出版社關聯的書籍對象集合    
publish_obj.book_set.all() :[obj1,obj2,....]

一對一查詢(Author---AuthorDetail)

正向查詢,按字段函數

author_obj.ad : 與這個做者關聯的做者詳細信息對象

反向查詢:按表名fetch

author_detail_obj.author : 與這個做者詳細對象關聯的做者對象

多對多(Book----Author)

正向查詢,按字段

book_obj.authorList.all(): 與這本書關聯的全部這做者對象的集合 [obj1,obj2,....]

book_obj.authorList.all().values("name"): 若是想查單個值的時候能夠這樣查

反向查詢,按表名_set

author_obj.book_set.all() : 與這個做者關聯的全部書籍對象的集合

book_obj.book_set.all().values("name"): 若是想查單個值的時候能夠這樣查

基於雙下滑線的跨表查詢(queryset對象查詢)

一對多查詢(Book--Publish)

正向查詢,按字段

# 查詢linux這本書的出版社的名字:
models.Book.objects.all().filter(title="linux").values("publish__name")

反向查詢:按表名

# 查詢人民出版社出版過的全部書籍的名字
models.Publish.objects.filter(name="人民出版社出版").values("book__title")

一對一查詢(Author---AuthorDetail)  

正向查詢,按字段

#查詢egon的手機號
models.Author.objects.filter(name="egon").values("ad__tel")

反向查詢:按表名

#查詢手機號是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:

  若是喲有設置,反向查詢的時候都用: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

概念原理

    利用 關聯管理器 進行維護

    • 外鍵關係的反向查詢
    • 多對多關聯關係

create()

    建立一個新的對象,保存對象,並將它添加到關聯對象集之中,返回新建立的對象。

models.Author.objects.first().book_set.create(title="羊駝之歌", publish_id=2)

add()

    把指定的model對象添加到關聯對象集中。

添加對象
>>> 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對象的關聯對象。

book_obj = models.Book.objects.first()
book_obj.authors.set([2, 3])

remove()

    從關聯對象集中移除執行的model對象

book_obj = models.Book.objects.first()
author_obj.books.remove(book_obj)
author_obj.books.remove(8)	  # 把id = 8 的書刪掉

clear()   

從關聯對象移除一切對象。

book_obj = models.Book.objects.first()
book_obj.authors.clear()

注意:

對於ForeignKey對象,clear()和remove()方法僅在null=True時存在。

# ForeignKey字段沒設置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'

# 當ForeignKey字段設置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()

注意

  對於全部類型的關聯字段,add()、create()、remove() 和 clear(), set() 都會立刻更新數據庫。

  換句話說,在關聯的任何一端,都不須要再調用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的手機號
 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     對應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的手機號
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     # 查詢手機號以151開頭的做者出版過的書籍名稱以及書籍對應的出版社名稱
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")
View Code

queryset對象特性及優化

特性: 

  惰性執行

books = BookInfo.objects.all() # 此時,數據庫並不會進行實際查詢
# 只有當真正使用時,如遍歷的時候,纔會真正去數據庫進行查詢
for b in books:
print(b)

  緩存

# 進行數據庫實際查詢遍歷,保存結果到bs,會進行數據庫實際交互
bs = [b.id for b in BookInfo.objects.all()]

# 再次調用緩存結果bs,再也不進行數據庫查詢,而是使用緩存結果

優化方法:

  exists()

  簡單的使用if語句進行判斷也會徹底執行整個queryset而且把數據放入cache,雖然你並不須要這些數據!爲了不這個,能夠用exists()方法來檢查是否有數據:

if queryResult.exists():
    #SELECT (1) AS "a" FROM "blog_article" LIMIT 1; args=()
    print("exists...")

  iterator()

  當queryset很是巨大時,cache會成爲問題。

  處理成千上萬的記錄時,巨大的queryset可能會鎖住系統進程,讓你的程序瀕臨崩潰。

  要避免在遍歷數據的同時產生queryset cache,可使用 iterator() 方法 來獲取數據,處理完數據就將其丟棄

objs = Book.objects.all().iterator() # iterator()能夠一次只從數據庫獲取少許數據,這樣能夠節省內存
for obj in objs:
    print(obj.title)

# 基於迭代器的特性,被遍歷到底部以後下次在使用是無效的了。
for obj in objs:
    print(obj.title)

  使用 .iterator() 來防止生成cache,意味着遍歷同一個queryset時會重複執行查詢.

  因此使 用 .iterator() 時需確保操做一個大queryset時沒有重複執行查詢.

總結:      

  Queryset的cache是用於減小程序對數據庫的查詢,在一般的使用下會保證只有在須要的時候纔會查詢數據庫。

  使用exists()和iterator()方法能夠優化程序對內存的使用。不過,因爲它們並不會生成queryset cache,可能 會形成額外的數據庫查詢。 

聚合

導入

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")

示例

# 每一個 「省」 的 「平均工資」 , 查詢後的結果爲 「省 : 平均工資 」
# annotate前面是什麼就按照什麼來分組,annotate後面的字段是被分組後被計算的新增數據列,
    
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=()
# """
# 統計每一本書的做者個數
book_list = models.Book.objects.all().annotate(author_num=Count("author"))

# 統計出每一個出版社買的最便宜的書的價格
publisher_list = models.Publisher.objects.annotate(min_price=Min("book__price"))
publisher_list = models.Book.objects.values("publisher__name").annotate(min_price=Min("price"))

# 統計不止一個做者的圖書
book_list = models.Book.objects.annotate(author_num=Count("author")).filter(author_num__gt=1)

# 根據一本圖書做者數量的多少對查詢集 QuerySet進行排序
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連表分組查詢
# 根據 "部門" 計算出 "平均工資" 結果爲顯示爲 "部門名字 : 平均工資" 的表

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=()
# """

查詢每一個部門的員工的員工數 

關鍵點:

  • queryset 對象.anntate() 
  • anntate 按前面的 select 字段進行 group by 分組統計 ,
  • anntate() 的返回值依舊是 queryset 對象 ,  只是增長了分組統計後的鍵值對
  • 即  " 分組規則 "  .anntate(" 連表操做,數據處理 ") . " 篩選字段 "
    • 分組規則 :
      • 基於queryset 方法 對分組關鍵字段進行篩選
        • 即 "根據"什麼
        • 轉換成 sql 語句 爲 group by 後面的部分
    • 連表操做,數據處理:
      • 視狀況進行是否連表,以及新增一個計算出的字段
        • " 計算 " 什麼
    • 篩選字段:
      • 基於queryset 方法 對新增字段進行篩選 並呈現最終結果
        • 即 "想要" 什麼
        • 轉換成 sql 語句 爲 select 後面的部分

分組查詢的超級詳細的解析示例:

  查詢每一個部門的員工總工總人數

F查詢

概念

    對於基礎的兩個值得比較能夠經過上面的方法實現

    可是對於兩個字段的比較則須要用到 F 查詢

示例

# 查詢評論數大於收藏數的書籍
from django.db.models import F
models.Book.objects.filter(commnet_num__gt=F('keep_num'))

# Django 支持 F() 對象之間以及 F() 對象和常數之間的加減乘除和取模的操做。
models.Book.objects.filter(commnet_num__lt=F('keep_num')*2)

# 對整個字段的全部值的操做也能夠經過 F 函數實現
# 好比將每一本書的價格提升30元
models.Book.objects.all().update(price=F("price")+30)

    關於修改 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查詢

概念

    當使用filter 的時候 ,內部多個篩選條件是 and 的關係

    若需求爲 or 的關係須要用到 Q 查詢

示例

# 查詢做者名是羊駝或山羊的
models.Book.objects.filter(Q(authors__name="羊駝")|Q(authors__name="山羊"))

複雜示例

# 能夠組合& 和| 操做符以及使用括號進行分組來編寫任意複雜的Q 對象。同時,Q 對象可使用~ 操做符取反,這容許組合正常的查詢和取反(NOT) 查詢。

# 查詢做者名字是羊駝之歌而且不是2018年出版的書的書名。 models.Book.objects.filter(Q(author__name="羊駝之歌") & ~Q(publish_date__year=2018)).values_list("title")

注意

    當 and 和 or 同時一塊兒用的時候 , Q 查詢須要放在前面

示例

# 查詢出版年份是2017或2018,書名中帶羊駝的全部書。
models.Book.objects.filter(Q(publish_date__year=2018) | Q(publish_date__year=2017), title__icontains="羊駝")

Q查詢的另外一種方法:

    此方法比一般使用稍微複雜一些。本質上和  .filter(Q(title="yang")|Q(price=123)) 實現效果相同,

    可是 這樣子拆分出來能夠實現 不在使用字段對象,而是用字符串來篩選

場景適用:

      搜索框獲取當前get 請求中的參數時,參數爲字符串形式,用常規的Q查詢必須依靠字段對象從而沒法實現

q = Q()                    # 將Q實例化對象單獨拿出來
q.connnection = "or"            # 默認多條件的關係是 "and" 經過connection 能夠改爲其餘 
q.children.append(("title", "yang"))  # 添加查詢字段
q.children.append(("price", 123))

  限制住當前查詢結束後才能夠其餘的操做.保證數據的可靠性

select_for_update(nowait=False, skip_locked=False)

示例

entries = Entry.objects.select_for_update().filter(author=request.user)

執行原生的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 # 執行徹底的原生的SQL語句,相似pymql 
2 from django.db import connection
3 cursor = connection.cursor()  # 獲取光標,等待執行SQL語句
4 cursor.execute("""SELECT * from person where id = %s""", [1])
5 row = cursor.fetchone()
6 print(row)
相關文章
相關標籤/搜索