口決:正向操做按字段,反向操做按表名小寫php
模型創建以下:python
from django.db import models # Create your models here. class Book(models.Model): title = models.CharField(max_length=32) pub_date = models.DateField() price = models.DecimalField(max_digits=5, decimal_places=2) publish = models.ForeignKey(to="Publish", to_field="id", on_delete=models.CASCADE, null=True) authors = models.ManyToManyField(to="Author", db_table="book2authors") def __str__(self): return self.title class Meta: db_table = "book" class Publish(models.Model): name = models.CharField(max_length=32) city = models.CharField(max_length=32) email = models.CharField(max_length=32) def __str__(self): return self.name class Meta: db_table = "publish" class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() ad = models.OneToOneField("AuthorDetail", null=True, on_delete=models.CASCADE) def __str__(self): return self.name class Meta: db_table = "author" class AuthorDetail(models.Model): birthday = models.DateField() telephone = models.BigIntegerField() addr = models.CharField(max_length=64) def __str__(self): return str(self.telephone) class Meta: db_table = "authorDetail"
注意事項:linux
關聯字段在建立表時字段名爲字段_idgit
先添加一的表:sql
publish = models.Publish.objects.create(name="蘋果出版社", city="深圳",email="1234@163.com")
再添加多的表:數據庫
方式一: book = models.Book.objects.create(title="Python", pub_date="2012-12-12", price=122, publish_id=1) 方式二: book = models.Book.objects.create(title="Python", pub_date="2012-12-12", price=122, publish=publish) #book.publish爲book書藉的出版社對象
與一對多同樣,先添加一的表,再添加帶關聯字段的表django
關聯字段在建立表時字段名爲字段_idide
# 正向(從具備關聯屬性的表添加) # Linux這本書綁定兩個做者:Tom, Linda # 方式一: linux = models.Book.objects.filter(title="Linux").first() tom = models.Author.objects.filter(name="Tom").first() linda = models.Author.objects.filter(name="Linda").first() linux.authors.add(tom, linda) # 方式二: linux = models.Book.objects.filter(title="Linux").first() linux.authors.add(1, 2)/linux.authors.add(*[1, 2]) # 反向(從不具備關聯屬性的表添加) # 給Ethan綁定一本書藉:PhP ethan = models.Author.objects.filter(name="Ethan").first() php = models.Book.objects.filter(title="PhP").first() ethan.book_set.add(php)
注:添加表記錄能夠經過插件(如navicat)完成函數
實質爲子查詢,即以上一次的查詢結果做爲下一次的查詢條件測試
正向查詢:按字段book.publish ----------------------------> Book對象 <---------------------------- Publish 對象 反向查詢:按表名小寫_set.all()
例: 正向:查詢Linux這本書藉的出版社的地址 book = models.Book.objects.filter(title="Linux").first() print(book.publish.city) 反向:查詢蘋果出版社出版的全部書藉 publish = models.Publish.objects.filter(name="蘋果出版社").first() print(publish.book_set.all())
正向查詢:按字段book.authors.all() ---------------------------------> Book對象 <--------------------------------- Author 對象 反向查詢:按表名小寫_set.all()
例: 正向:查詢Linux書藉的全部做者 linux = models.Book.objects.filter(title="Linux").first() print(linux.authors.all()) 反向:查詢tom出版過的全部書藉 tom = models.Author.objects.filter(name="Tom").first() print(tom.book_set.all())
正向查詢:按字段book.ad ---------------------------------> Author 對象 <--------------------------------- AuthorDetail 對象 反向查詢:按表名小寫
例: 正向:查詢Tom的地址 tom = models.Author.objects.filter(name="Tom").first() print(tom.ad.addr) 反向:查詢地址爲南京的做者的名字 ad = models.AuthorDetail.objects.filter(addr="南京").first() print(ad.author.name)
Django還提供了一種直觀而高效的方式在查詢(lookups)中表示關聯關係,它能自動確認SQL JOIN聯繫。要作跨關係查詢,就使用兩個下劃線來連接模型(model)間關聯字段的名稱。直到最終連接到你想要的model爲止。
實質爲join查詢。
"""正向查詢按字段,反向查詢按表名小寫"""
# <1> 查詢Linux這本書藉的出版社的地址(一對多) # 正向查詢 addr = models.Book.objects.filter(title="Linux").values("publish__city") # 反向查詢 addr = models.Publish.objects.filter(book__title="Linux").values("city") # <2> 查詢Linux書藉的全部做者名字(多對多) # 正向查詢
print(models.Book.objects.filter(title="Linux").values("authors__name")) # 反向查詢
print(models.Author.objects.filter(book__title="Linux").values("name"))
# <3> 查詢Ethan的手機號(一對一) # 正向查詢 ret = models.Author.objects.filter(name="Ethan").values("ad__telephone") # 反向查詢 ret = models.AuthorDetail.objects.filter(author__name="Ethan").values("telephone")
# <1> 查詢人民出版社出版過的全部書藉的名字以及做者的姓名 # 正向查詢 ret = models.Book.objects.filter(publish__name="教育出版社").values("title", "authors__name") # 反向查詢 ret = models.Publish.objects.filter(name="人民出版社").values_list("book__title", "book__authors__name")
# <2> 手機號以13開頭的做者出版過的全部書籍名稱以及出版社名稱 # 方式一: ret = models.Book.objects.filter(authors__ad__telephone__startswith="13").values("title", "publish__name")) # 方式二: ret = models.Author.objects.filter(ad__telephone__startswith="13").values("book__title", "book__publish__name")
反向查詢時,若是定義了related_name,則用related_name替換表名,如:
# models.py class Book(models.Model): publish = models.ForeignKey(to="Author", to_field="id", on_delete=models.CASCADE, null=True, related_name="booklist")
# 查詢人民出版社出版過的全部書籍的名字與價格(一對多) # 反向查詢 再也不按表名:book,而是related_name:bookList ret =models.Publish.objects.filter(name="人民出版社").values_list("bookList__title","bookList__price")
方法:aggregate(*args, **kwargs)
導入聚合函數:
from django.db.models import Avg,Max,Min,Sum,Count
調用者:QuerySet對象
返回值:包含一些鍵值對的字典。鍵的名稱若是沒有指定,則按照字段和聚合函數的名稱自動生成,也可自行指定
# 計算全部圖書的平均價格 from django.db.models import Avg # 自動生成鍵名稱 models.objects.aggregate(Avg("price")) # {'price_avg': 34.55} # 自行指定鍵名稱 models.objects.aggregate(average_price = Avg("price")) # {'average_price': 34.55}
能夠向aggregate()添加多個參數,即生成多個聚合
from django.db.models import Avg, Max, Min models.Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) # {'price__avg': 34.55, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
本質:將關聯表join成一張表,再安單表的思路進行分組查詢
KEY: 1. 選連表(基於雙下劃線查詢就是連表),再Group by,最後顯示字段 2. values().annotate(),此時values()就是group by
方法:annotate()
調用者:QuerySet對象
返回值:QuerySet對象
###################################--單表分組查詢--####################################################### 查詢每個部門名稱以及對應的員工數 emp: id name age salary dep 1 alex 12 2000 銷售部 2 egon 22 3000 人事部 3 wen 22 5000 人事部 sql語句: select dep,Count(*) from emp group by dep; ORM: emp.objects.values("dep").annotate(c=Count("id") ###################################--多表分組查詢--########################### 多表分組查詢: 查詢每個部門名稱以及對應的員工數 emp: id name age salary dep_id 1 alex 12 2000 1 2 egon 22 3000 2 3 wen 22 5000 2 dep id name 1 銷售部 2 人事部 emp-dep: id name age salary dep_id id name 1 alex 12 2000 1 1 銷售部 2 egon 22 3000 2 2 人事部 3 wen 22 5000 2 2 人事部 class Emp(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() salary=models.DecimalField(max_digits=8,decimal_places=2) dep=models.CharField(max_length=32) province=models.CharField(max_length=32) sql語句: select dep.name,Count(*) from emp left join dep on emp.dep_id=dep.id group by dep.id ORM: dep.objetcs.values("id").annotate(c=Count("emp")).values("name","c")
# objects.values().annotate()與objects.all().annotate()區別 dep.objects.values("id").annotate(c=Count("price")) # 獲得的是QuerySet集合,集合裏元素爲id和c鍵值對的字典 dep.objects.all().annotate(c=Count("price")) # 獲得的是QuerySet集合,集合裏元素爲dep對象,dep對象在原有基礎上多了個c字段
# <1> 統計每個出版社的最便宜的書 # <2> 統計每一本書的做者個數 # <3> 統計每一本以Py開頭的書籍的做者個數 # <4> 統計不止一個做者的圖書 # <5> 根據一本圖書做者數量的多少對查詢集QuerySet進行排序 # <6> 查詢各個做者出的書的總價格
# <1> 統計每個出版社的最便宜的書 ret = models.Publish.objects.annotate(MinPrice=Min("book__price")).values_list("name", "MinPrice") # <2> 統計每一本書的做者個數 ret = models.Book.objects.annotate(authorsNum=Count("authors__name")) # <3> 統計每一本以Py開頭的書籍的做者個數 ret = models.Book.objects.filter(title__startswith="Py").annotate(num_authors=Count("authors")) # <4> 統計不止一個做者的圖書 ret = models.Book.objects.annotate(num_authors=Count("authors")).filter(num_authors__gt=1) # <5> 根據一本圖書做者數量的多少對查詢集QuerySet進行排序 ret = models.Book.objects.annotate(num_authors=Count("authors")).order_by("num_authors") # <6> 查詢各個做者出的書的總價格 ret = models.Author.objects.annotate(SumPrice=Sum("book__price")).values_list("name", "SumPrice")
在上面的全部例子中,構造的過濾器都只是將字段值與某個常量作比較
Django提供F()的實例能夠在查詢中引用字段,來比較同一個model實例中兩個不一樣的字段的值
F的導入:
from django.db.models import F
from django.db.models import F # 查詢評論數大於收藏數的書籍 ret = models.Book.objects.filter(commentNum__lt=F("keepNum"))
Django支持F()對象之間以及F()對象和常數之間的加減乘除和取模的操做
# 查詢評論數大於收藏數2倍的書籍 ret = models.Book.objects.filter(commentNum__lt=F("keepNum")*2)
修改操做也可使用F函數
# 將每一本書的價格提升30元 ret = models.Book.objects.all().update(price=F("price") + 30)
filter()等方法中的關鍵字參數查詢都是一塊兒進行"and"的。若是須要執行更復雜的查詢(例如OR 語句),則使用Q對象
Q查詢支持查詢條件的and,or, not操做,分別對應操做符&,|,~
Q的引入:
from django.db.models import Q
Q對象可使用&和|操做符組合起來。當一個操做符在兩個Q對象上使用時,它產生一個新的Q對象
# 查詢價格大於300或者名稱以"p"開頭的書籍 ret = models.Book.objects.filter(Q(price__gt=300)|Q(title__startswith="p"))
能夠組合&和|操做符以及使用括號進行分組來編寫任意複雜的Q對象。同時,Q對象可使用~操做符取反,這容許組合正常的查詢和取反(NOT)查詢:
# 查詢由Ethan編寫的且不是在2017年出版的書籍名稱 ret = models.Book.objects.filter(Q(authors__name="Ethan") & ~Q(pub_date__year=2017)).values_list("title")
查詢函數能夠混合使用Q對象和關鍵字參數。全部提供給查詢函數的參數(關鍵字參數或Q對象)都將"AND"在一塊兒。可是,若是出現Q對象,它必須位於全部關鍵字參數的前面
# 查詢由2016或2017年出版的名稱中含有"python"(不區分大小寫)的書籍 ret = models.Book.objects.filter(Q(pub_date__year=2016) | Q(pub_date__year=2017), title__icontains="python")
當有多個操做符時,屬於同一關係外層還需套一層Q
# 查詢價格大於300或者不是2019年1月出版的書籍 ret = models.Book.objects.filter(Q(price__gt=300) | ~Q(Q(pub_date__year=2019) & Q(pub_date__month=1)))
# create, add, remove, clear, set適用於以下管理器: """ 1. 一對多:如Publish_obj.book_set 2. 多對多:如Book_obj.authors, Author_obj.book_set """
# 刪除Linux這本書的一個做者Tom linux = models.Book.objects.filter(title="Linux").first() tom = models.Author.objects.filter(name="Tom").first() linux.authors.remove(tom)/tom.book_set.remove(linux) # 刪除第三張表Tom與Linux記錄
# 刪除Linux這本書的全部做者 linux = models.Book.objects.filter(title="Linux").first() linux.authors.clear()
至關於clear + add。set括號內參數爲list
# 刪除Linux這本書的全部做者,並添加Ethan爲做者 # 方法一: linux = models.Book.objects.filter(title="Linux").first() linux.authors.set([3,]) # 方法二: linux = models.Book.objects.filter(title="Linux").first() ethan = models.Author.objects.filter(name="Ethan").first() linux.authors.set([ethan,])
# 一、 自行建立測試數據; # 二、 查詢學生總人數; # 三、 查詢「生物」課程和「物理」課程成績都及格的學生id和姓名; # 四、 查詢每一個年級的班級數,取出班級數最多的前三個年級; # 五、 查詢平均成績最高的學生的id和姓名以及平均成績; # 六、 查詢每一個年級的學生人數; # 七、 查詢每位學生的學號,姓名, 平均成績; # 八、 查詢學生編號爲「2」的學生的姓名、該學生成績最高的課程名及分數; # 九、 查詢姓「李」的老師的個數和所帶班級數; # 十、查詢班級數小於5的年級id和年級名; # 十一、查詢教過課程超過2門的老師的id和姓名; # 十二、查詢學過編號「1」課程和編號「2」課程的同窗的學號、姓名; # 1三、查詢所帶班級數最多的老師id和姓名; # 1四、查詢有課程成績小於60分的同窗的學號、姓名; # 1五、查詢男生、女生的人數,按倒序排列; # 1六、查詢各個課程及相應的選修人數; # 1七、查詢同時選修了物理課和生物課的學生id和姓名; # 1八、檢索「3」課程分數小於60,按分數降序排列的同窗學號; # 1九、查詢每門課程的平均成績,結果按平均成績升序排列,平均成績相同時,按課程號降序排列; # 20、查詢各科成績最高和最低的分:以以下形式顯示:課程ID,最高分,最低分;
# 一、 自行建立測試數據; # 二、 查詢學生總人數; ret = models.Student.objects.aggregate(studentNum=Count("*")) # 三、 查詢「生物」課程和「物理」課程成績都及格的學生id和姓名; ret = models.Score.objects.filter(Q(course__cname="物理")|Q(course__cname="生物"), score__gt=59).values("student").annotate(c=Count("*")).filter(c__gt=1).values("student__sid", "student__sname") # 四、 查詢每一個年級的班級數,取出班級數最多的前三個年級; ret = models.Cls.objects.values("grade_id").annotate(c=Count("cid")).order_by("-c").values("grade__gname")[:3] # 五、 查詢平均成績最高的學生的id和姓名以及平均成績; ret = models.Score.objects.values("student_id").annotate(avg=Avg("score")).order_by("-avg").values("student__sid", "student__sname", "avg").first() # 六、 查詢每一個年級的學生人數; ret = models.Student.objects.values("cls__grade").annotate(c=Count("cls__grade")).values("cls__grade__gname", "c") # 七、 查詢每位學生的學號,姓名, 平均成績; ret = models.Score.objects.values("student_id").annotate(avg=Avg("score")).values("student__sid", "student__sname", "avg") # 八、 查詢學生編號爲「2」的學生的姓名、該學生成績最高的課程名及分數; ret = models.Score.objects.filter(student_id=2).order_by("-score").values("student__sname", "course__cname", "score").first() # 九、 查詢姓「李」的老師的個數和所帶班級數; ret = models.Teacher.objects.filter(tname__startswith="李").annotate(c=Count("tid")).values("c", "classes") # 十、查詢班級數小於5的年級id和年級名; ret = models.Class_Grade.objects.values("gid").annotate(c=Count("cls__grade_id")).filter(c__lt=5).values("gid", "gname") # 十一、查詢教過課程超過2門的老師的id和姓名; ret = models.Course.objects.values("teacher_id").annotate(c=Count("*")).filter(c__gt=1).values("teacher__tid", "teacher__tname") # 十二、查詢學過編號「1」課程和編號「2」課程的同窗的學號、姓名; ret = models.Score.objects.values("student_id").annotate(c=Count("*")).filter().values("student__sid", "student__sname") # 1三、查詢所帶班級數最多的老師id和姓名;***** ret = models.Teacher.objects.annotate(c=Count("classes__cid")).order_by("-c").values("tid", "classes__teacher__tname")[0] # 1四、查詢有課程成績小於60分的同窗的學號、姓名; ret = models.Score.objects.values("student").annotate(min=Min("score")).filter(score__lt=60).values("student__sid", "student__sname") # 1五、查詢男生、女生的人數,按倒序排列; ret = models.Student.objects.values("gender").annotate(c=Count("sid")).order_by("-c") # 1六、查詢各個課程及相應的選修人數; ret = models.Score.objects.values("course").annotate(c=Count("*")).values("course__cname","c") # 1七、查詢同時選修了物理課和生物課的學生id和姓名; ret = models.Score.objects.filter(Q(course__cname="物理")|Q(course__cname="生物")).values("student").annotate(c=Count("*")).filter(c__gt=1).values("student__sid", "student__sname") # 1八、檢索「3」課程分數小於60,按分數降序排列的同窗學號; ret = models.Score.objects.filter(score__lt=60, course_id="3").order_by("-score").values("student__sid") # 1九、查詢每門課程的平均成績,結果按平均成績升序排列,平均成績相同時,按課程號降序排列; ret = models.Score.objects.values("course").annotate(c=Avg("score")).order_by("c", "-course__cid").values("course__cname", "c") # 20、查詢各科成績最高和最低的分:以以下形式顯示:課程ID,最高分,最低分; ret = models.Score.objects.values("course").annotate(max=Max("score"), min=Min("score")).values("course__cid", "max", "min")
表關係: