1.單表操作和測試環境的準備
我們先對單表查詢做一個總結和回顧,並進行進一步的學習和交流。我們在我們的應用的models.py檔案下面書寫user類。如下所示,然後用資料庫遷移,在mysql資料庫中生成表。然後進行資料庫表的單表查詢的操作。
# models.py檔案
class User(models.Model):
username = models.CharField(max_length=32, verbose_name='使用者名稱')
password = models.CharField(max_length=32, verbose_name='密碼')
age = models.IntegerField(verbose_name='年齡')
info = models.CharField(max_length=256, verbose_name='使用者描述')
salary = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='使用者的薪水')
register_time = models.DateField(default='2020-8-28',verbose_name='註冊時間')
'''
這裡補充一個知識點:DateField和DateTimeField有兩個重要的引數
這兩個引數分別是:
auto_now:每次運算元據的時候,該欄位會自動將當前時間更新
auto_now_add:在建立資料的時候會自動將當前建立時間記錄下來,之後只要不修改,那麼時間一直不變。
'''
def __str__(self):
return '%s' % self.username
我們之前學了單表操作的知識,在這裡我們鞏固一下,並且進行更深入一步的學習。我們之前測試我們的models檔案是否正常,資料庫遷移是否正確,我們都是採用的是前後端互動的技術實現測試的。但是我們知道django的orm語句是比較多的,我們每次對於資料庫的操作,orm語句的操作都需要前後端來測試的話,那麼這個是十分的麻煩。在這裡我們在學習單表操作的同時,也引進了關於測試環境準備的知識點,那麼以後我們寫好的.py檔案的程式碼,我們可以放到我們的測試環境中,即test.py檔案中進行測試。指令碼程式碼無論是寫在自己應用下的test.py中,還是自己單獨開設.py檔案都可以。
測試環境的準備:去manager.py中copy這個檔案的前面四行程式碼,然後自己寫兩行。測試檔案是通用的一個功能,不一定是在應用下的test.py檔案,自己也可以新建測試的.py檔案,例如mytest.py檔案。
在這個程式碼塊下面就可以測試django裡面的單個py檔案了。所有的程式碼都要寫在測試環境的下面,因為測試環境還沒有準備好,我們必須放在測試環境的下面,等待測試環境準備好之後,我們才可以開始測試。所有的程式碼必須等待環境準備好之後才可以開始書寫。
# 測試環境的搭建
import os
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'model_mysite.settings')
import django
django.setup()
if __name__ == '__main__':
main()
現在我們在我們的測試環境下來測試、使用、學習和擴充套件我們的單表查詢。
# 這個匯入包的操作必須放在測試環境之後,即django.setup()之後,不然會出現報錯。
from app01 import models
# 單表操作--增
# 用create()方法增加
models.User.objects.create(username='zhouqian', password='123456', age=22, info='可甜、可憐、可愛、可傻', salary=12500,register_time='2020-6-26')
# 用類的方式增加,save()方法
user_obj = models.User(username='AndreasZhou', password='456789', age=22, info='可騷、可變態、博多、倉老師', salary=22500,register_time='2020-6-28')
user_obj.save()
# 單表操作--刪
models.User.objects.filter(id=1).delete()
# 查詢出某一個資料,然後將這個資料刪除,直接呼叫deldete()方法
# 單表操作--改
# 批量的更新操作。這個其實是所謂的批量的更新的操作,但是因為我們平常用的id值唯一的主鍵,所以我們這裡更新的其實也是一個資料
models.User.objects.filter(id=2).update(username='ZhouQian')
# 單獨的更新操作
user_obj = models.User.objects.filter(id=3).first()
user_obj.username = 'ANDREASZHOU'
user_obj.save()
# 單表操作--查
res = models.User.objects.filter(id=2) # <QuerySet [<User: User object (2)>]> 類似於列表套物件的形式 <QuerySet [<User: ZhouQian>]>
print(res)
user_obj = models.User.objects.filter(id=2).first()
print(user_obj) # User object (2) ZhouQian
res = models.User.objects.all()
print(res) # <QuerySet [<User: User object (2)>, <User: User object (3)>]> <QuerySet [<User: ZhouQian>, <User: ANDREASZHOU>]>
'''
在這裡我們需要補充一個知識點就是pk的使用,pk會自動查詢到當前表的主鍵欄位,指代的就是當前表的主鍵欄位,用了pk之後,你就不需要指代當前表的主鍵欄位到底叫什麼了。uid,pid,sid...。所以我們在平時的使用的時候,我們用pk用的比較的頻繁。所以我們後面按照主鍵查詢,我們就按照pk來使用就好了。
'''
2.必知必會13條(重點)
1)all():查詢所有
res = models.User.objects.all() # 得到的是一個查詢集queryset,類似於列表裡面套資料物件
print(res) # <QuerySet [<User: User object (2)>, <User: User object (3)>]> <QuerySet [<User: ZhouQian>, <User: ANDREASZHOU>]>
2)filter():帶有過濾條件的查詢
res = models.User.objects.filter(id=2) # <QuerySet [<User: User object (2)>]> 類似於列表套物件的形式 <QuerySet [<User: ZhouQian>]>
print(res)# 獲取的也是queryset,類似於列表套資料物件
user_obj = models.User.objects.filter(id=2).first()
print(user_obj) # User object (2) ZhouQian 列印出來的直接是一個物件。因為這裡顯示zhouqian表示的是呼叫了類中的__str__方法了。
3)get():直接拿資料物件,但是條件不存在那麼會直接報錯,不推薦使用。
user_obj = models.User.objects.get(id=2)
print(user_obj) # 得到的是User object(2) ZhouQian
user_obj = models.User.objects.get(id=4) # app01.models.DoesNotExist: User matching query does not exist. 查詢不存在,所以這裡報錯,其實我們的get方法是不推薦使用的,完全被filter方法給替代了。
print(user_obj)
'''
get方法返回的直接就是當前的資料物件,但是該方法不推薦使用,一旦資料不存在該方法會直接報錯。
而filter則不會報錯,所以我們還是用filter
'''
4)first():拿到queryset裡面的第一個元素,即第一個資料物件(queryset是列表套資料物件)
res = models.User.objects.filter(id=2) # <QuerySet [<User: User object (2)>]> 類似於列表套物件的形式 <QuerySet [<User: ZhouQian>]>
print(res)
user_obj = models.User.objects.filter(id=2).first()
print(user_obj) # User object (2) ZhouQian
5)last():拿到queryset裡面的最後一個元素,即最後一個資料物件(queryset是列表套資料物件)
res = models.User.objects.filter(id=2) # <QuerySet [<User: User object (2)>]> 類似於列表套物件的形式 <QuerySet [<User: ZhouQian>]>
print(res)
user_obj = models.User.objects.filter(id=2).last()
print(user_obj) # User object (2) ZhouQian
user_obj = models.User.objects.all().last()
print(user_obj) # ANDREASZHOU
6)values():可以指定的獲取的資料欄位 select name,age from ..... 類似於列表套字典
res = models.User.objects.values('name','age')
# 類似於sql語句中的select name,age from User;
print(res)
# 列印出來的結果是一個queryset,這裡的queryset我們可以看做是列表套字典的形式,返回結果我們是需要記憶的。記住這是一個返回結果queryset,列表裡面套字典的形式,字典的關鍵字是name和age。
# select username,password from User;
res = models.User.objects.values('username','password')
print(res)
7)values_list():可以指定的獲取的資料欄位 select name,age from ..... 類似於列表套元組
res = models.User.objects.values('name','age')
print(res)
# 列印出來的結果是一個queryset,這裡的queryset我們可以看做是列表套元組的形式,返回結果我們是需要記憶的。記住返回的是一個queryset,裡面是列表套元組的形式。
# select username,password from User;
res = models.User.objects.values('username','password')
print(res)
print(res.query)
# 在這裡我們補充一個知識點
'''
檢視內部封裝的sql語句
上述檢視sql語句的方式,只能用於QuerySet物件
只有queryset物件才能夠點選query檢視內部的sql語句
'''
8)distinct():去重
res = models.User.objects.values('username','age').distinct()
print(res)
select distinct username,age from User;
"""
去重一定要是一模一樣的資料,如果帶有主鍵那麼肯定不一樣,你在往後的查詢中一定不要忽略主鍵
"""
9)order_by():排序
res = models.User.objects.order_by('age') # 預設是升序
print(res)
res = models.User.objects.order_by('-age') # 現在是降序
print(age)
res = models.User.objects.all().order_by('age') # 升序
print(res)
10)reverse():反轉,先排序,才可以反轉,是建立在排序的基礎上的
res = models.User.objects.all()
res1 = models.User.objects.order_by('age').reverse()
print(res,res1)
11)count():統計數量
res = models.User.objects.count()
print(res)
12)exclude():除了什麼之外,排除在外。
res = models.User.objects.exclude(name='jason')
print(res)
13)exists():判斷是否存在,返回的是布林值,基本用不到因為資料本身就自帶布林值。
res=models.User.objects.filter(pk=10).exists()
print(res)
3.神器的雙下劃線查詢
1.我們要你幫我查出年齡大於35歲的資料
# 我們寫的sql語句是:
select * from user where age >35
res = models.User.objects.filter(age__gt=35)
print(res) # <QuerySet [<User: az>]> 返回的是一個字典,列表裡面套資料物件
2.我們要你幫我查出年齡小於35歲的資料
select * from user where age < 35
res = models.User.objects.filter(age__lt=35)
print(res) # <QuerySet [<User: ZhouQian>, <User: ANDREASZHOU>]> 返回的是一個字典,列表裡面套資料物件。
3.我們要你幫我查出年齡大於等於35歲的資料
select * from user where age >=35;
res = models.User.objects.filter(age__gte=35)----->這裡的g是greater,t是than,e是equal
print(res) # <QuerySet [<User: ak>, <User: az>]>。返回的是一個queryset,類似於列表裡面套資料物件。
4.我們要你幫我查出年齡小於等於35歲的資料
select * from user where age <=35;
res = models.User.objects.filter(age__lte=35)
print(res) # <QuerySet [<User: ZhouQian>, <User: ANDREASZHOU>, <User: ak>]>返回的是一個queryset,類似於列表裡面套資料物件。
5.我們要你幫我查出年齡是18或者35或者38
select * from user where age in (18,35,38);
res = models.User.objects.filter(age__in=(18, 35, 38))
print(res) # <QuerySet [<User: ak>, <User: az>]>
6.我們要你幫我查出年齡在18到48歲之間的,首位都要
res = models.User.objects.filter(age__range=[18, 48])
print(res) # <QuerySet [<User: ZhouQian>, <User: ANDREASZHOU>, <User: ak>, <User: az>]>返回的是一個queryset,類似於列表裡面套資料物件。
7.我們要你幫我查出名字裡面含有n的資料,模糊查詢,區分大小寫
# 預設是區分大小寫的
res = models.User.objects.filter(username__contains='n')
print(res) # <QuerySet [<User: ZhouQian>]> 返回的是一個queryset,類似於列表裡面套資料物件。
8.我們要你幫我查出名字裡面含有n的資料,模糊查詢,忽略大小寫
# 忽略大小寫
res = models.User.objects.filter(username__icontains='n')
print(res) # <QuerySet [<User: ZhouQian>, <User: ANDREASZHOU>]> 返回的是一個queryset,類似於列表裡面套資料物件。
9.我們要你幫我查出名字以Z為開頭
res = models.User.objects.filter(username__startswith='Z')
print(res) # <QuerySet [<User: ZhouQian>]> 返回的是一個queryset,類似於列表裡面套資料物件。
10.我們要你幫我查出名字以U為結尾
res = models.User.objects.filter(username__endswith='U')
print(res) # <QuerySet [<User: ANDREASZHOU>]> 返回的是一個queryset,類似於列表裡面套資料物件。
11.我們要你幫我查出註冊時間的月
res = models.User.objects.filter(register_time__month=8)
print(res) # <QuerySet [<User: ak>, <User: az>]>
12.我們要你幫我查出註冊時間的年
res = models.User.objects.filter(register_time__year=2020)
print(res) # <QuerySet [<User: ZhouQian>, <User: ANDREASZHOU>, <User: ak>, <User: az>]>
4.多表查詢
我們在做多表查詢的之前需要做一些準備工作,下面就是我們準備工作要寫的模型類,將寫好的模型類遷移到資料庫中,這樣我們就可以開始做我們的多表的查詢。模型類的程式碼如下所示。
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name='書名')
price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='價格')
publish_date = models.DateField(auto_now_add=True, verbose_name='出版時間')
publish = models.ForeignKey(to='Publish', verbose_name='書和出版社多對一',on_delete=models.CASCADE)
author = models.ManyToManyField(to='Author', verbose_name='書和作者多對多')
class Publish(models.Model):
name = models.CharField(max_length=32, verbose_name='出版社名稱')
addr = models.CharField(max_length=32, verbose_name='出版社地址')
email = models.EmailField(verbose_name='聯絡郵箱')
# EmailField其實和varchar(254)是一樣的。
# 該欄位型別不是給models看的,而是給我們後面會學到的校驗性元件看的。
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name='作者姓名')
age = models.IntegerField(verbose_name='作者年齡')
author_detail = models.OneToOneField(to='AuthorDetail', verbose_name='作者和作者詳情一對一',on_delete=models.CASCADE)
class AuthorDetail(models.Model):
phone = models.CharField(max_length=32, verbose_name='手機號')
# 電話號碼我們用BigIntegerField或者使用直接使用CharField,而不能用IntegerField。
addr = models.CharField(max_length=32, verbose_name='作者地址')
5.外來鍵的增刪改
我們前面已經建立了表之間的關係,那麼我們現在可以使用orm框架實現外來鍵的增刪改查,因為查是最難的知識點,所以我們將查和增刪改分開講解,我們先講解增刪改,後面再講解查。
1)一對一、一對多外來鍵的增刪改
# 一對一、一對多外來鍵的增
# 增:
models.Book.objects.create(title='白蛇傳', price=255, publish_id=3) # 1.直接寫id的形式增加
publish_obj = models.Publish.objects.filter(pk=2).first() # 2.用物件的形式增加
models.Book.objects.create(title='武當派',price=388,publish=publish_obj)
# 一對一、一對多外來鍵的刪
# 刪:我們設定為級聯更新,級聯刪除
models.Publish.objects.filter(pk=1).delete()
# 一對一、一對多外來鍵的改
# 改:
models.Book.objects.filter(pk=7).update(publish_id=3) # 1.直接寫id的形式更新
publish_obj = models.Publish.objects.filter(pk=1).first() # 2.用物件的形式更新
models.Book.objects.filter(pk=7).update(publish=publish_obj)
2)多對多外來鍵增刪改清空
接下來我們學習多對多外來鍵的增刪改,我們依次用下面的程式碼來寫:
# 多對多外來鍵的增
# 直接寫id的形式增加
book_obj = models.Book.objects.filter(pk=7).first()
print(book_obj.author) # app01.Author.None 就類似於你已經進入了第三張表的關係了
book_obj.author.add(1) # 書籍id為1的書籍繫結一個主鍵為1的作者
book_obj = models.Book.objects.filter(pk=8).first()
print(book_obj.author) # app01.Author.None 就類似於你已經進入了第三張表的關係了
book_obj.author.add(1, 2, 3, 4)
# 用物件的形式增加
author_obj = models.Author.objects.filter(pk=2).first()
author_obj1 = models.Author.objects.filter(pk=3).first()
author_obj2 = models.Author.objects.filter(pk=4).first()
book_obj = models.Book.objects.filter(pk=4).first()
book_obj1 = models.Book.objects.filter(pk=5).first()
print(book_obj.author) # app01.Author.None 就類似於你已經進入了第三張表的關係了
book_obj.author.add(author_obj, author_obj1, author_obj2)
book_obj1.author.add(author_obj)
'''
add()給第三張關係表新增資料,括號內既可以傳數字也可以傳物件,並且支援一個或者多個。
'''
# 多對多外來鍵的刪
# 直接寫id的形式刪除
book_obj = models.Book.objects.filter(pk=5).first()
print(book_obj.author) # app01.Author.None 類似於進入了第三張表
book_obj.author.remove(2)
# 用物件的形式刪除
author_obj = models.Author.objects.filter(pk=2).first()
author_obj1 = models.Author.objects.filter(pk=3).first()
book_obj = models.Book.objects.filter(pk=4).first()
book_obj.author.remove(author_obj,author_obj1)
'''
remove():括號內既可以傳數字也可以傳物件,並且數字和物件可以有一個或者是多個。
'''
# 多對多外來鍵的改----記住這裡的set方法只可以傳入一個可迭代的物件,不然會報錯。可迭代物件的列表和元組都是可以的。
# 直接寫id的形式修改
book_obj = models.Book.objects.filter(pk=4).first()
book_obj.author.set([2]) # 這個括號內必須是一個可迭代物件,列表和元組都是可以的
book_obj.author.set((3,))
book_obj.author.set([2])
# 用物件的形式修改
book_obj = models.Book.objects.filter(pk=7).first()
author_obj = models.Author.objects.filter(pk=3).first()
author_obj1 = models.Author.objects.filter(pk=4).first()
book_obj.author.set([author_obj, author_obj1]) # 這裡面必須是一個可迭代物件,列表和元組都是可以的。
'''
set():set的括號內必須是一個可迭代的物件,可迭代物件裡面既可以是數字也可以是物件,並且數字和物件可以有一個或者是多個。
'''
# 多對多的清空---清空的clear()方法中不需要傳入任何的引數和物件。我們只要點這個方法就可以。
# 清空:在第三張關係表中清空某個書籍與作者的繫結關係
book_obj = models.Book.objects.filter(pk=7).first()
book_obj.author.clear()
'''
clear():括號內不需要加任何的引數
'''
6.正反向的概念
在我們講解多表查詢的時候,我們在這裡先學一個概念,這個概念就是關於正反向的概念,如果我們弄清楚了這個概念以後,我們的下面的學下就會表的更加的輕鬆。
外來鍵欄位在我手上,那麼我查你就是正向。外來鍵欄位如果不在我手上,那麼我查你就是反向。
-
book 》》》外來鍵欄位在書哪兒(正向)》》》publish
-
publish》》》外來鍵欄位在書哪兒(反向)》》》book
一對一,多對多正反向的判斷也是如此
7.多表查詢--基於物件的跨表查詢(子查詢)
正向查詢按欄位,如果我們發現結果是有多個的時候我們是欄位加上all(),如果發現結果只有一個的時候,我們是欄位即可。
反向查詢按表名小寫,如果發現結果有多個的時候我們是表名小寫加__set
,並且還要加上all()。如果是單個的時候我們是直接表名小寫,也不用加__set和all()
1)正向查詢
1.查詢書籍主鍵為1的出版社名稱
書籍查出出版社 正向
book_obj = models.Book.objects.filter(pk=1).first()
res = book_obj.publish
print(res) # 列印出來的是出版社物件
print(res.name) # 北京出版社
print(res.addr) # 北京
這個res是一個Publish物件,我們列印呼叫了`__str__`方法,顯示出是物件:北京出版社。
2.查詢書籍主鍵為2的作者
書籍查作者是正向查詢
book_obj = models.Book.objects.fliter(pk=2).first()
res = book_obj.author
print(res) # 列印出來是app01.Author.None
res = book_obj.author.all() # 列印出很多個作者物件
3.查詢作者jason的電話號碼
作者到作者的詳情,所以是正向。所以我們這裡這樣寫:
author_obj = models.Author.objects.filter(name='jason').first()
res=author_obj.author_detail
print(res) 得到的結果是一個AuthorDetail物件。
print(res.phone)
print(res.addr)
上面我們得到了一個特點是:利用orm跨表查詢是比較的簡單。在書寫orm語句的時候跟寫sql語句是一樣的,不要企圖一次性將orm語句寫完,如果比較複雜,就寫一點看一點,分段寫
正向的時候什麼時候需要加.all()呢?當你的結果可能有多個的時候就需要加.all(),如果是一個則直接拿到資料物件。
2)反向查詢
4.查詢出版社是北京出版社出版的書籍
出版社查書 反向
publish_obj = models.Publish.objects.filter(name='北京出版社').first()
res = publish_obj.book_set
print(res) # app01.Book.None
res = publish_obj.book_set.all()
print(res) 返回的是queryset,我們可以理解為列表裡面套物件
5.查詢作者是jason寫過的書
Author 到 book 是反向查詢
author_obj = models.Author.objects.filter(name='jason').first()
res = author_obj.book_set app01.Book.None
res = author_obj.book_set.all()
6.查詢手機號是110的作者姓名
authoudetail 到 author 是反向查詢 AuthorDetail-->Book是反向的查詢
author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
res = author_detail_obj.author
print(res.name)
基於物件反向查詢的時候的規律
當你的查詢結果可以有多個的時候,我們就必須表名小寫加上_set.all()
當你的查詢結果只有一個的時候,我們就必須是表名小寫,不需要加_set.all()
8.多表查詢--基於雙下劃線的跨表查詢(聯表查詢)
例子1.查詢書籍主鍵為1的出版社名稱(一行程式碼搞定)
select Publish.name from (select * from Book,Publish where publish_id = Publish.id ) as Book_Publish where Book.id =1;
1.查詢jason的手機號和作者姓名
正向
res=models.Author.objects.filter(name='json').values('author_detail__phone',‘name’)
print(res) # query_set:類似於列表裡面套字典
反向
res = model.AuthorDetail.objects.filter(author__name='jason') 拿作者姓名jason的作者詳情
res = model.AuthorDetail.objects.filter(author__name='jason').values('phone','author__name')
print(res)
2.查詢書籍主鍵為1的出版社名稱和書的名字
正向
# 書籍查出版社是正向
res = models.Book.objects.filter(pk=1).values('publish__name','title')
print(res)
反向
res = models.Publish.objects.filter(book__id=1).values('name','book__title')
print(res)
3.查詢書籍主鍵為1的作者姓名
正向
res = models.Book.objects.filter(pk=1).values('author__name')
print(res) # queryset 類似於列表裡面套字典
反向
res= models.Author.objects.filter(book__pk=1).values('name')
print(res) # queryset 類似於列表裡面套字典
4.查詢書籍主鍵是1的作者的手機號
Book Author AuthorDetail
res = models.Book.objects.filter(pk=1).values('author__author_detail__phone')
print(res)
你只要掌握了正反向的概念以及雙下劃線的查詢,那麼你就可以無限制的跨表查詢,聯表查詢。
9.聚合查詢
在這裡我們先學習一個東西,我們必須明白一個理論的知識。這個理論知識就是:只要是跟資料庫相關的模組基本上都在django.db.models裡面,如果上述沒有那麼應該在django.db裡面。聚合函式涉及的一般是如下:max,min,sum,count,avg,在django中,我們應該使用aggregate()。
from app01 import models
from django.db.models import Max,Min,Count,Sum,Avg
# 1.計算所有書的平均價格
res = models.Book.objects.aggregate(Avg('price'))
print(res)
# 2.上訴的方法統一使用
res=models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Count('pk'),Avg('price'))
print(res) # 返回的是字典
{'price__max': Decimal('388.00'), 'price__min': Decimal('255.00'), 'pk__count': 5, 'price__avg': Decimal('334.800000'), 'price__sum': Decimal('1674.00')}
10.分組查詢
分組在mysql中是group by,在我們的django是用annotate作為分組的使用。一般分組和聚合是在一起使用的。兩者相輔相成,也就是我們平時所說的分組聚合。
MySQL分組查詢有哪些特點呢?分組時候預設只能獲取到分組的依據,組內其他欄位無法直接獲取了。因為MySQL預設是嚴格模式,ONLY_FULL_GROUP_BY
我們看下面的幾個題目來深刻的體會和理解分組查詢的意義和內涵。
# 1.統計每一本書的作者的個數
# models後面點什麼我們就按照什麼來分組,models.Book.objects.values('price').annotate()按照書的價格分組
# res = models.Book.objects.annotate(author_number=Count('author__id')).values('title', 'author_number')
# print(res)<QuerySet [<Book: Book object (3)>, <Book: Book object (4)>, <Book: Book object (5)>, <Book: Book object (7)>, <Book: Book object (8)>]>
# print(res)
# < QuerySet[{'title': '小斌張嘎', 'author_number': 0}, {'title': '白蛇傳', 'author_number': 1}, {'title': '武當派',
# 'author_number': 0}, {
# 'title': '聊齋', 'author_number': 2}, {'title': '鬥破蒼穹', 'author_number': 4}] >
# 2.統計每一個出版社賣的最便宜的書的價格
# res = models.Publish.objects.annotate(book_price=Min('book__price')).values('name', 'book_price')
# print(res)
# < QuerySet[{'name': '上海出版社', 'book_price': Decimal('388.00')}, {'name': '杭州出版社', 'book_price': Decimal('255.00')}, {
# 'name': '北京出版社', 'book_price': Decimal('255.00')}, {'name': '嘉興出版社', 'book_price': None}] >
# 3.統計不止是一個作者的圖書
# res = models.Book.objects.annotate(author_num=Count('author__pk')).filter(author_num__gt=1).values('title', 'author_num')
# print(res) # <QuerySet [{'title': '聊齋', 'author_num': 2}, {'title': '鬥破蒼穹', 'author_num': 4}]>
# 4.查詢每個作者出的書的總價格
# res = models.Author.objects.annotate(book_sum=Sum('book__price')).values('name', 'book_sum')
# print(res)
# < QuerySet[{'name': 'AndreasZhou', 'book_sum': Decimal('643.00')}, {'name': '週週', 'book_sum': Decimal('643.00')}, {
# 'name': '憨憨', 'book_sum': Decimal('643.00')}, {'name': '周乾', 'book_sum': Decimal('388.00')}] >
在這裡我們補充一個知識點,這個知識點就是:只要你的orm語句得出來的結果還是一個QuerySet物件,那麼它就可以繼續無限制的點QuerySet物件封裝的方法。
還有一個知識點就是,如果我想要按照指定的欄位分組該如何處理呢?
models.Book.objects.values('price').annotate(),後續我們在寫專案的時候會使用到這個知識點。
11.F查詢
我們現在來講解一下什麼是F查詢,F查詢的用途是什麼呢?
F查詢:能夠幫助你直接獲取到列表中某個欄位對應的資料
1.查詢賣出數大於庫存數的書籍
from django.db.models import F
res = models.Book.objects.filter(maichu__gt=F('庫存'))
print(res)
# <QuerySet [<Book: Book object (3)>, <Book: Book object (7)>]>
2.將所有書籍的價格提升50塊
# 2.將所有書籍的價格提升50塊
from django.db.models import F
res = models.Book.objects.all().values('title', 'price')
print(res)
res = models.Book.objects.update(price=F('price') + 50)
print(res)
models.Book.objects.update(price=F('price')+50)
F('price'):表示獲取的是原來的價格
3.將所有書的名稱後面加上爆款兩個字(瞭解)
'''
在操作字串型別的資料的時候,F不能夠直接做到字串的拼接。
'''
from django.db.models.functions import concat
from django.db.models import Value
models.Book.objects.update(title=concat((F('title'),Value('爆款')))
12.Q查詢
在學習Q查詢之前,我們先來說明一個問題,filter預設只是支援使用的是與(and)關係。但是往往這樣一種關係是不能滿足於我們平時的使用。所以我們需要更多的邏輯關係,使得我們更加的操作方便,因此我們在這裡引入了一種查詢方式,這個查詢方式叫做Q查詢。
1.查詢賣出數大於100或者價格小於600的書籍
`res = models.Book.objects.filter(maichu__gt=100,price__lt=600)`
`print(res)`
'''
filter括號內多個引數是and關係,是我們想要的是或,非等關係,那我們怎麼辦呢?
'''
from django.db.models import Q
`res = models.Book.objects.filter(Q(maichu__gt=100,price__lt=600))` Q包裹,逗號分隔,還是and關係
`res = models.Book.objects.filter(Q(maichu__gt=100|price__lt=600))` Q包裹,|分隔,or關係
`res = models.Book.objects.filter(~Q(maichu__gt=100,price__lt=600))` Q包裹,~分隔,not關係
`print(res)`
2.Q的高階用法 能夠將查詢條件左邊也變成字串的形式,而不再是變數名的形式。使用Q我們可以實現一個簡單的搜尋功能。
q = Q()
q.connector='or'
q.children.append(('maichu__gt',100))
q.children.append(('price__lt',600))
res=models.Book.objects.filter(q) # filter括號內也支援直接放q物件,預設還是and關係
print(res)
13.django中如何開啟事務
事務:我們知道事務有四個特性,這四個特性分別是:
原子性:不可分割的最小單位。
一致性:跟原子性是相輔相成的。
隔離性:事務之間是互相不干擾。
永續性:事務一旦確認是永久有效的。
事務的回滾:rollback
事務的確認:commit
from django.db import transaction
try:
with transaction.atomic():
# sql1
# sql2
# ...
# 在with程式碼塊內書寫的所有orm操作都是同一個事務
except Exception as e:
print(e)
print('執行其他操作')
14.orm中常用欄位以及引數
# 常用欄位
AutoField()
IntegerField()
CharField()
DecimalField()
EmailField()
DateField()
DateTimeField()
TimeField()
OneToOneField()
ManyToManyField()
ForeignField()
# 常用引數
null
unique
verbose_name
default
auto_now
auto_now_add
to
to_field
on_delete
max_length
choices
# 瞭解欄位
SmallIntegerField()
PositiveSmallIntegerField()
PositiveIntegerField()
PositiveIntegerField()
BooleanField()
# 瞭解引數
related_name
through
through_fields
db_table
參考:https://www.cnblogs.com/liuqingzheng/articles/9627915.html
15.choices引數(資料庫欄位設計常見)
'''
使用者表
姓名
學歷
工作經驗
是否生子
是否結婚
客戶來源
...
針對某個可以列舉完全的可能性欄位,我們該如何儲存?
只要某個欄位的可能性是可以列舉完全的,那麼一般情況下都會採用choices引數
'''
class User(models.Model):
username = models.CharField(max_length=32,verbose_name='使用者名稱')
age = models.IntegerField(verbose_name='年齡')
gender_choices = (
(1,'男'),
(2,'女'),
(3,'其他'),
)
gender = models.IntegerField(choices=gender_choices)
'''
改gender欄位存的還是數字,但是如果存的數字在上面元組列舉的範圍之內,那麼可以非常輕鬆的獲取到數字對應的真正內容。
1.gender欄位存的數字不在上述元組列舉的範圍內容
2.如果在的話,如何獲取對應的中文資訊
'''
from app01 import models
models.User.objects.create(username='zhouqian',age=18,gender=1)
models.User.objects.create(username='zhoukun',age=28,gender=2)
models.User.objects.create(username='AndreasZhou',age=20,gender=3)
models.User.objects.create(username='thanzhou',age=18,gender=4)
user_obj = models.User.objects.filter(pk=1).first()
print(user_obj.gender)
user_obj = models.User.objects.filter(pk=1).first()
# 只要是choices引數的欄位,如果想要獲取對應資訊。固定寫法:get_欄位名_display()
print(user_obj.get_gender_display())
# 如果沒有對應關係,那麼欄位是什麼還是展示什麼
user_obj = models.User.objects.filter(pk=4)..first()
print(user_obj.get_gender_display()) # 4
gender_choices=(
(1,'男'),
(2,'女'),
(3,'其他'),
)
gender = models.IntegerField(choices=gender_choices)
scores_choices = (
('A','優秀'),
('B','良好'),
('C','及格'),
('D','不及格'),
)
# 保證欄位的型別跟列舉出來的元組的第一個資料型別一致即可。
score = models.CharField(choices=scores_choices,null=True)
16.多對多三種建立方式
# 系統自帶
class Book(models.Model):
name = models.CharField(max_length=32)
authors = models.ManyToManyField(to='Author')
class Author(models.Model):
name = models.CharField(max_length=32)
'''
優點:程式碼不需要你寫,非常方便,還支援orm提供操作第三張關係表的方法,add(),remove(),clear(),set()
不足之處:第三張關係表的擴充套件性極差(沒有第三張表,沒有辦法額外新增欄位)
'''
# 純手動
class Book(models.Model):
name = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
class Book2Author(models.Model):
book_id = models.ForeignKey(to='Book')
author_id = models.ForeignKey(to='Author')
'''
優點:第三張表完全取決於你自己進行額外的擴充套件
不足之處:需要你自己手寫程式碼,寫的程式碼比較多。不能夠使用orm提供的簡單的方法,不建議你用該方式。
'''
# 半自動
class Book(models.Model):
name = models.CharField(max_length=32)
authors = models.ManyToManyField(to='Author',
through='Book2Author'
through_fields=('book','author')
)
class Author(models.Model):
name = models.CharField(max_length=32)
class Book2Author(models.Models):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
'''
半自動:可以使用orm的正反向查詢,但是沒發使用add,set,remove,clear這四個方法
'''
總結:需要掌握全自動和半自動,為了擴充套件性更高,一般我們都會採用半自動(寫程式碼給自己留一條後)。