Web框架之Django_05 模型层了解(单表查询、多表查询、聚合查询、分组查询)
閱讀目錄
- 一、Django ORM 常用字段和參數:
- 二、單表查詢
- 三、多表查詢
- 基于雙下劃線的多表查詢
- 四、聚合查詢和分組查詢
摘要:
- 單表查詢
- 多表查詢
- 聚合查詢
- 分組查詢
一、Django ORM 常用字段和參數:
1:常用字段:
#AutoField
int自增列,必須填入參數primary_key = True,當model中如果沒有自增列,則會自動創建一個列名為id的列
#IntegerField
一個整數類型,范圍在–2147483648 to 2147483647。(一般不用它來存手機號(位數也不夠),直接用字符串存)
#CharField
字符類型,必須提供max_length參數,max_length表示字符長度
Tips:Django的CharField對應的MySQL數據庫中的varchar類型,沒有設置對應char類型的字段,但是Django允許我們自定義新的字段
需要注意的是:自定義字段在實際項目應用中可能會經常用到,所以這里需要留意。
#DateField
日期字段,日期格式 YYYY-MM-MD,例如:2019-6-12,相當于Python中的datetime.date()實例。
#DateTimeField
日期格式字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相當于Python中的datetime.datetime()實例。
2:字段合集:
字段合集
ORM字段與MySQL字段對應關系
3:字段參數:
#null :用于表示某個字段可以為空
#unique :如果設置為unique = True 則該字段在此表中必須是唯一的
#db_index :如果db_index = True 則代表著為此字段設置索引
#default :為該字段設置默認值
4:DateField和DateTimeField:
#auto_now_add 配置auto_now_add = True,創建數據記錄的時候會把當前時間添加到數據庫,后續操作數據不自動更新
#auto_now 配置上auto_now = True,每次更新數據記錄的時候會更新該字段
5:關系字段:
#①ForeignKey 外鍵類型在ORM中用表示外鍵關聯關系,一般吧ForeignKey字段設置在‘一段多’中的多的一方,ForeignKey可以和其它表做關聯關系同時也可以和自身做關聯關系。
#字段參數:
##to 設置要關聯的表
##to_field 設置要關聯的表的字段
##on_delete 當刪除關聯表中的數據時,當前表與其關聯的行的行為 models.CASCADE (Django2.x版本必須要設置,1.x版本默認就設置了) 刪除關聯數據,與之關聯也刪除
##db_constraint 是否在數據庫中創建外鍵約束,默認為True
示例:
def func():return 10class MyModel(models.Model):user = models.ForeignKey(to="User",to_field="id", # 不寫默認關聯對方關聯表的primary_keyon_delete=models.SET(func))#②OneToOneField
一對一字段
通常一對一字段用來擴展已有字段。(通俗的說就是一個人的所有信息不是放在一張表里面的,簡單的信息一張表,隱私的信息另一張表,之間通過一對一外鍵關聯)
#字段參數:
##to 設置要關聯的表
##to_field 設置要關聯的表的字段
##on_delete 當刪除關聯表中的數據時,當前與其關聯的行的行為(參考ForeignKey)
#③ManyToManyField
多對多字段
#字段參數:
##to 設置要關聯的表
##to_field 設置要關聯的表的字段
##on_delete 當刪除關聯表中的數據時,當前與其關聯的行的行為(參考ForeignKey)
二、單表查詢
- 準備
為了更方便的測試表查詢的結果,我們需要先配置一下參數,可以是我們直接在Django頁面中運行我們的測試表查詢代碼,快速顯示結果,所以需要做如下操作:
項目根目錄創建一個test.py測試文件>>>>在項目的manage.py文件中復制一段代碼>>>>將復制的代碼粘貼到test.py文件中,然后再加入幾段代碼,這樣就可以在test文件中進行數據庫表數據操作了,當然,連接數據庫的過程不能少,表的創建也是。
這里需要建一張表,用于單表查詢:(models文件內)
- 新增表數據
- 修改表數據
- 刪除表數據
總結一下單表操作的一些方法:
<1> all(): 查詢所有的結果,返回QuerySet對象們的列表
<2> filter(**kwargs): 通過括號中的條件查詢到匹配的對象,返回QuerySet對象們的列表
res = models.User.objects.filter(name='sgt') print(res) <QuerySet [<User: User object>, <User: User object>, <User: User object>]>這里提一下:filter篩選條件是可以有多個的,多個條件是與關系,必須全部滿足才會得到查詢結果<3> get(**kwargs): 返回與所給篩選條件匹配的object對象,且返回結果只有一個,如果符合篩選條件的對象超過一個或者沒有都會拋出
res1 = models.User.objects.get(name='sgt') # app01.models.MultipleObjectsReturned: get() returned more than one User -- it returned 3! res2 = models.User.objects.get(name='孫悟空') # app01.models.DoesNotExist: User matching query does not exist. res3 = models.User.objects.get(name='王大錘') # User object<4> exclude(**kwargs): 返回篩選條件不匹配的QuerySet對象們的列表(取反篩選)
res1 = models.User.objects.exclude(name='sgt') <QuerySet [<User: User object>, <User: User object>, <User: User object>]><5> order_by(*field): 對查詢結果通過某一字段進行排序(默認升序),返回QuerySet對象們的列表
res1 = models.User.objects.filter(name='sgt').order_by('age') # 排序默認是升序,如果這里的age前加上一個負號‘-’,怎會進行降序排序。 # res1 = models.User.objects.filter(name='sgt').order_by('-age') print(res1) # <QuerySet [<User: User object>, <User: User object>, <User: User object>]> # 為了讓結果更清晰,遍歷下得到排好序的QuerySet對象們列表,以其age顯示順序 users = [] for user_obj in res1:users.append(user_obj.age) print(users) # [18, 22, 32]<6> reverse(): 對查詢的排序結果進行反向排列,記住,不是反向排序,而是反向排列,返回QuerySet對象們的列表
res1 = models.User.objects.filter(name='sgt').order_by('age').reverse() users = [] for user_obj in res1:users.append(user_obj.age) print(users) # 結果反過來排列了:[32, 22, 18]<7> count(): QuerySet方法,返回查詢到的QuerySet對象的數量,該方法只能對返回的查詢結果為QuerySet對象使用,如果返回的對象
res1 = models.User.objects.filter(name='sgt').count() print(res1) # 3res2 = models.User.objects.filter(name='hahaha').count() print(res2) # 0res3 = models.User.objects.get(name='王大錘').count() # 拋出錯誤:AttributeError: 'User' object has no attribute 'count'<8> first(): 返回查詢結果的第一個對象,返回一個普通對象(User object)
res = models.User.objects.filter(name='sgt').first() print(res) # User object<9> last(): 返回查詢結果的最后一個對象,返回一個普通對象(User object)
res = models.User.objects.filter(name='sgt').last() print(res) # User object<10> exists(): QuerySet方法, 如果QuerySet包含數據,就返回True,否則返回False
res = models.User.objects.filter(name='sgt').exists() print(res) # True res = models.User.objects.filter(name='sgg').exists() print(res) # False<11> values(*field): 返回一個特殊的QuerySet對象,對象里面是一個列表套字典(model的實例化對象,而是一個可迭代的字典序列)
res = models.User.objects.filter(name='sgt').values('age') print(res) print(res.order_by('age')) print(res.order_by('age').reverse()) print(res.count()) print(res[0]) print(res.first(), res.last())<QuerySet [{'age': 18}, {'age': 32}, {'age': 22}]> # values方法返回結果 <QuerySet [{'age': 18}, {'age': 32}, {'age': 22}]> # 通過age來反向排列,無效,說明該方法只能用在order_by之后,才能進行反向排列 <QuerySet [{'age': 18}, {'age': 22}, {'age': 32}]> # order_by <QuerySet [{'age': 32}, {'age': 22}, {'age': 18}]> # order_by后再reverse 3 # 使用count方法 {'age': 18} # 可以用索引取出對應索引的字典 {'age': 18} {'age': 22} # 使用first和last方法<12> values_list(*field): 它與values()非常相似,它返回的是一個元組序列的QuerySet對象
res = models.User.objects.filter(name='sgt').values_list('age') print(res) print(res.order_by('age')) print(res.order_by('age').reverse()) print(res.count()) print(res[0]) print(res.first(), res.last())<QuerySet [(18,), (32,), (22,)]> <QuerySet [(18,), (22,), (32,)]> <QuerySet [(32,), (22,), (18,)]> 3 (18,) (18,) (22,)結果和values方法差不多,只是把字典序列變成了元祖序列<13> distinct(): 從返回結果中剔除重復紀錄,注意必須是所有數據都相同才滿足去重條件,但是一個表中的每一條數據是不會完全重復的,因為有primary_key的存在,就不會完全相同,所以此方法的主要是在上面的values和values_list方法的基礎上用的話會實現一些我們想要的結果。返回特殊的QuerySet對象
res1 = models.User.objects.filter(name='sgt') res2 = models.User.objects.filter(name='sgt').distinct() print(res1) print(res2) # <QuerySet [<User: User object>, <User: User object>, <User: User object>, <User: User object>]> # <QuerySet [<User: User object>, <User: User object>, <User: User object>, <User: User object>]> # 結果數量不變,說明沒有去重的項目,主要原因:id沒有相同的,肯定無法去重# 我們用values方法的基礎上使用distinct方法: res1 = models.User.objects.filter(name='sgt').values('age') res2 = res1.distinct() print(res1) print(res2)# <QuerySet [{'age': 18}, {'age': 32}, {'age': 22}, {'age': 22}]> # <QuerySet [{'age': 18}, {'age': 32}, {'age': 22}]> # 發現這樣去重的意義就出來了只要是QuerySet對象都可以無線的點其中的方法,點下去…
包含(區分大小寫__contains|不區分大小寫__icontains),以xx開頭(__startswith),以xx結尾(__endswith)
res1 = models.User.objects.filter(name__contains='i').values('name', 'age') print(res1) # <QuerySet [{'name': 'igon', 'age': 55}, {'name': 'igoN', 'age': 66}]> res2 = models.User.objects.filter(name__contains='N').values('name', 'age') print(res2) # <QuerySet [{'name': 'igoN', 'age': 66}]> res0 = models.User.objects.filter(name__icontains='j').values('name', 'age') print(res0) # <QuerySet [{'name': 'json', 'age': 11}, {'name': 'Jason', 'age': 16}]> res3 = models.User.objects.filter(name__startswith='j').values('name', 'age') print(res3) # <QuerySet [{'name': 'json', 'age': 11}]> res4 = models.User.objects.filter(name__endswith='t').values('name', 'age') print(res4) # <QuerySet [{'name': 'sgt', 'age': 18}]>時間為2018年的記錄:(__year=2017)
res = models.User.objects.filter(register_time__year=2018).values('name', 'age', 'register_time') print(res) # <QuerySet [{'name': 'json', 'age': 11, 'register_time': datetime.date(2018, 6, 21)}]>在指定容器內__in=[a,b,c],在指定范圍內__range=[m,n],首尾都包括在內
res1 = models.User.objects.filter(age__in=[66, 88]).values('name', 'age') print(res1) # <QuerySet [{'name': '王大炮', 'age': 66}, {'name': '黃飛鴻', 'age': 88}, {'name': 'igoN', 'age': 66}]> res2 = models.User.objects.filter(age__range=[9, 12]).values('name', 'age') print(res2) # <QuerySet [{'name': '鐵頭', 'age': 9}, {'name': '佩奇', 'age': 12}, {'name': 'json', 'age': 11}]>三、多表查詢
- 表與表之間的關系
#一對一(OneToOneField):一對一字段無論建在哪張關系表里面都可以,但是推薦建在查詢頻率比較高的那張表里面
#一對多(ForeignKey):一對多字段建在多的那一方
#多對多(ManyToManyField):多對多字段無論建在哪張關系表里面都可以,但是推薦建在查詢頻率比較高的那張表里面 多對多操作:
添加add(),
修改set(),
移除remove() 注意不能接收可迭代對象,
清空clear() 無需傳參
如何判斷表與表之間到底是什么關系 換位思考
A能不能有多個B
B能不能有多個A
- 多表的增刪改查:
增刪改:一對多
Book與Publish 一對多關系
新增:
修改:
# 一對多修改# QuerySet方法修改 ① models.Book.objects.filter(pk=1).update(publish_id=2) ② publish_obj = models.Publish.objects.filter(pk=4).first() models.Book.objects.filter(pk=1).update(publish=publish_obj)# 對象方法修改 ① book_obj = models.Book.objects.filter(pk=1).first() book_obj.publish_id = 3 # 點表中真實存在的字段名 book_obj.save() ② book_obj = models.Book.objects.filter(pk=1).first() publish_obj = models.Publish.objects.filter(pk=1).first() book_obj.publish = publish_obj book_obj.save()刪除:(默認情況下)
# 多對一刪除 ①QuerySet方法 # 當刪除'多'的表數據,則那張'一'的表無影響 models.Book.objects.filter(pk=1).delete()# 當刪除'一'的表數據,則那張'多'的表對應的記錄會刪除 models.Publish.objects.filter(pk=2).delete()②對象方法: book_obj = models.Book.objects.filter(pk=2).first() book_obj.delete()增刪改:多對多
book與author表之間是多對多關系
新增
修改
# 修改book和author記錄及其關系:set()方法,必須傳可迭代對象 book_obj = models.Book.objects.filter(pk=3).first() # 修改方法和新增類似,也是修改自身數據后再修改關系 # 可以傳數字和對象,并且也支持傳多個 # 傳id book_obj.author.set((1,)) # 傳一個 book_obj.author.set((1,2,3)) # 傳多個 # 傳對象 author_list = models.Author.objects.all() book_obj = models.Book.objects.filter(pk=3).first() book_obj.author.set(author_list)刪除:默認情況下刪除一方表記錄,連帶的會刪除其綁定關系,但是另一邊記錄無影響,僅僅是綁定關系沒有了
# 多對多刪除 # QuerySet方法 models.Book.objects.filter(pk=4).delete() # 刪除id為4的書,同時會自動刪除此書與作者之間的關系,此時綁定關系會自動刪除# 對象方法 book_obj = models.Book.objects.filter(pk=5).first() book_obj.author.remove(2) # 先刪除關系不會刪除book記錄數據book_obj = models.Book.objects.filter(pk=6).first() book_obj.delete() # 刪除book對象,會自動觸發刪除綁定關系# 假設一本書有多個作者 book_obj = models.Book.objects.filter(pk=1).first() author_list = models.Author.objects.all() book_obj.author.remove(*author_list) # 注意這里的QuerySet需要打散才行增刪改:一對一
# 一對一,author表創建外鍵綁定關系到authordetail表,刪除創建外鍵的那張author表的記錄,另一方無影響 models.Author.objects.filter(pk=1).delete() # 一對一,刪除非外鍵的那張authordetail表記錄,則綁定關系以及對應的author表記錄都會隨之刪除 models.AuthorDetail.objects.filter(pk=2).delete()新增:
# 一對一新增:創建外鍵的表author中新增記錄,對應的外鍵必須未用占且存在,因為是一對一關系,否則報錯 models.Author.objects.create(name='王錘', age=88, authordetail_id=1) # 一對一新增,authordetail表中新增記錄,author表無影響. models.AuthorDetail.objects.create(phone='123', addr='毛里求斯')修改:
# 一對一修改, models.Author.objects.filter(pk=8).update(name='湯勺') models.AuthorDetail.objects.filter(pk=4).update(addr='伊拉克')# 一對一修改 關聯外鍵值: # 外鍵值必須未占用,且存在,不然報錯 models.Author.objects.filter(pk=8).update(authordetail_id=5)清空clear()
book_obj = models.Book.objects.filter(pk=3).first() book_obj.authors.clear() # 清空當前書籍對應的所有作者的綁定關系正向查詢:通過一張表a查詢另一張表b,如果a與b之間的關系外鍵是建在a中,那么我們從a中能通過這個外鍵字段查詢到b表中的對應數據的過程就叫做正向查詢
反向查詢:上面的正向查詢,如果從b表去找a表的數據,外鍵還是建在a表中,這時候b表中就沒有與a表關聯的字段,此時b查到a就需要用另外的手段來實現,這種就叫反向查詢
正向、反向查詢方法:
原則:正向查詢按字段,反向查詢按表名小寫
一對一
Author表和AuthorDetail表是一對一關系:(外鍵在Author表中)
一對多
#多對多
# 正向查詢 res1 = models.Book.objects.filter(pk=2).first().author.all() print(res1) # <QuerySet [<Author: Author object>, <Author: Author object>]> res2 = models.Book.objects.filter(pk=2).first().author.all().values('name') print(res2) # <QuerySet [{'name': '金庸'}, {'name': '湯勺'}]> # 反向查詢 res3 = models.Author.objects.filter(pk=3).first().book_set.all() print(res3) # <QuerySet [<Book: Book object>, <Book: Book object>]> res4 = models.Author.objects.filter(pk=3).first().book_set.all().values('title') print(res4) # <QuerySet [{'title': '西廂記'}, {'title': '紅樓夢'}]>規律:一對多、多對多 反向查詢的時候要加_set.all()
一對一 反向查詢不用加
基于雙下劃線的多表查詢
用例子來說明:
① 查詢書籍為紅樓夢的出版社名:
② 查詢書籍為西游記的作者的姓名:
res = models.Book.objects.filter(title='西游記').values('author__name') print(res) # <QuerySet [{'author__name': '三毛'}, {'author__name': '唐唐'}]>③ 查詢南方出版社初版的書名:
res = models.Publish.objects.filter(name='南方出版社').values('book__title') print(res) # <QuerySet [{'book__title': '紅樓夢'}, {'book__title': '西游記'}]>④ 查詢電話號碼是911的作者寫的書名:
res = models.AuthorDetail.objects.filter(phone='911').values('author__book__title') print(res) # <QuerySet [{'author__book__title': '三國志'}, {'author__book__title': '西廂記'}]>⑤ 查詢作者名字為金庸寫的書的書名:
res = models.Author.objects.filter(name='金庸').values('book__title') print(res) # <QuerySet [{'book__title': '三國志'}, {'book__title': '西廂記'}]>⑥ 查詢書籍為西游記的作者的電話號碼:
res = models.Book.objects.filter(title='西游記').values('author__name', 'author__authordetail__phone') print(res) # <QuerySet [{'author__name': '三毛', 'author__authordetail__phone': '119'}, # {'author__name': '唐唐', 'author__authordetail__phone': '110'}]>⑦ 通過作者細節表 查詢作者叫金庸的電話號碼
res = models.AuthorDetail.objects.filter(author__name='金庸').values('phone') print(res) # <QuerySet [{'phone': '911'}]>⑧ 從書這張表 查詢出版社為南方出版社的所有圖書的名字和價格
res = models.Book.objects.filter(publish__name='南方出版社').values('title', 'price') print(res) # <QuerySet [{'title': '紅樓夢', 'price': Decimal('67.00')}, {'title': '西游記', 'price': Decimal('87.00')}]>⑨ 查詢東方出版社初版的書的價格大于45的書
res = models.Publish.objects.filter(name='東方出版社', book__price__gt=45).values('book__title', 'book__price') print(res) # <QuerySet [{'book__title': '三國志', 'book__price': Decimal('48.00')}]>四、聚合查詢和分組查詢
- 聚合查詢 aggregate(),它是QuerySet的一個終止子句,意思就是,會返回一個包含一些鍵值對的字典
鍵的名稱是聚合值的標識符,值是計算出來的聚合值。鍵的名稱是按照字段和聚合函數的名稱自動生成出來的 用到的內置函數:
示例:
from django.db.models import Avg,Sum,Max,Min,Count、 查詢所有書籍的平均價格,最大價格,書的數量 res = models.Book.objects.all().aggregate(Avg('price'), Max('price'), Count('title')) print(res) # {'price__avg': 56.6, 'price__max': Decimal('87.00'), 'title__count': 5}可以給聚合值指定一個名稱,用于使用該聚合值
如果你想要為聚合值指定一個名稱,可以向聚合子句提供它。>>> models.Book.objects.aggregate(average_price=Avg('price')) {'average_price': 13.233333}- 分組查詢 示例: 統計每一個出版社的出版的書平均價格
統計出每個出版社買的最便宜的書的價格
統計不止一個作者的圖書
res = models.Book.objects.annotate(author_num=Count('author')).filter(author_num__gt=1).values('title') print(res)# <QuerySet [{'title': '西廂記'}]>總結
以上是生活随笔為你收集整理的Web框架之Django_05 模型层了解(单表查询、多表查询、聚合查询、分组查询)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Web框架之Django_04 模板层了
- 下一篇: Web框架之Django_06 模型层了