一、創建模型
表和表之間的關系
一對一、多對一、多對多 ,用book表和publish表自己來想想關系,想想里面的操作,加外鍵約束和不加外鍵約束的區別,一對一的外鍵約束是在一對多的約束上加上唯一約束(OnetoOne = foreign key + unique)。
實例:我們來假定下面這些概念,字段和關系
作者模型:一個作者有姓名和年齡。
作者詳細模型:把作者的詳情放到詳情表,包含生日,手機號,家庭住址等信息。作者詳情模型和作者模型之間是一對一的關系(one-to-one)
出版商模型:出版商有名稱,所在城市以及email。
書籍模型: 書籍有書名和出版日期,一本書可能會有多個作者,一個作者也可以寫多本書,所以作者和書籍的關系就是多對多的關聯關系(many-to-many);一本書只應該由一個出版商出版,所以出版商和書籍是一對多關聯關系(one-to-many)。
模型建立如下:
from django.db import models # Create your models here. class Author(models.Model): #比較常用的信息放到這個表里面 nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) age=models.IntegerField() # 與AuthorDetail建立一對一的關系,一對一的這個關系字段寫在兩個表的任意一個表里面都可以 authorDetail=models.OneToOneField(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE) #就是foreignkey+unique,只不過不需要我們自己來寫參數了,並且orm會自動幫你給這個字段名字拼上一個_id,數據庫中字段名稱為authorDetail_id # authorDetail = models.OneToOneField(to="AuthorDetail", to_field="nid", on_delete=models.SET_NULL, null=True) # 取消級聯,且字段需設置可以是null class AuthorDetail(models.Model):#不常用的放到這個表里面 nid = models.AutoField(primary_key=True) birthday=models.DateField() telephone=models.BigIntegerField() addr=models.CharField( max_length=64) class Publish(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) city=models.CharField( max_length=32) email=models.EmailField() #多對多的表關系,我們學mysql的時候是怎么建立的,是不是手動創建一個第三張表,然后寫上兩個字段,每個字段外鍵關聯到另外兩張多對多關系的表,orm的manytomany自動幫我們創建第三張表,兩種方式建立關系都可以,以后的學習我們暫時用orm自動創建的第三張表,因為手動創建的第三張表我們進行orm操作的時候,很多關於多對多關系的表之間的orm語句方法無法使用 #如果你想刪除某張表,你只需要將這個表注銷掉,然后執行那兩個數據庫同步指令就可以了,自動就刪除了。 class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField( max_length=32) publishDate=models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2) # 與Publish建立一對多的關系,外鍵字段建立在多的一方,字段publish如果是外鍵字段,那么它自動是int類型 publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE) #foreignkey里面可以加很多的參數,都是需要咱們學習的,慢慢來,to指向表,to_field指向你關聯的字段,不寫這個,默認會自動關聯主鍵字段,on_delete級聯刪除 字段名稱不需要寫成publish_id,orm在翻譯foreignkey的時候會自動給你這個字段拼上一個_id,這個字段名稱在數據庫里面就自動變成了publish_id # 與Author表建立多對多的關系,ManyToManyField可以建在兩個模型中的任意一個,自動創建第三張表,並且注意一點,你查看book表的時候,你看不到這個字段,因為這個字段就是創建第三張表的意思,不是創建字段的意思,所以只能說這個book類里面有authors這個字段屬性 authors=models.ManyToManyField(to='Author',) #注意不管是一對多還是多對多,寫to這個參數的時候,最后后面的值是個字符串,不然你就需要將你要關聯的那個表放到這個表的上面
補充知識:
qq = models.CharField(verbose_name='QQ號', max_length=64, unique=True, help_text='QQ號必須唯一') # verbose_name:當前字段的一個描述信息,verbose_name可省略不寫,直接字段里用“QQ號”,help_text:幫助信息提示
# 自關聯:
introduce_from = models.ForeignKey('self', verbose_name="轉介紹自學員", blank=True,null=True) # self指的就是自己這個表,和下面寫法是一樣的效果
關於多對多表的三種創建方式
方式一:自行創建第三張表
class Book(models.Model): title = models.CharField(max_length=32, verbose_name="書名") class Author(models.Model): name = models.CharField(max_length=32, verbose_name="作者姓名") # 自己創建第三張表,分別通過外鍵關聯書和作者 class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") class Meta: unique_together = ("author", "book")
方式二:通過ManyToManyField自動創建第三張表
class Book(models.Model): title = models.CharField(max_length=32, verbose_name="書名") # 通過ORM自帶的ManyToManyField自動創建第三張表 class Author(models.Model): name = models.CharField(max_length=32, verbose_name="作者姓名") books = models.ManyToManyField(to="Book", related_name="authors")
方式三:設置ManyTomanyField並指定自行創建的第三張表
class Book(models.Model): title = models.CharField(max_length=32, verbose_name="書名") # 自己創建第三張表,並通過ManyToManyField指定關聯 class Author(models.Model): name = models.CharField(max_length=32, verbose_name="作者姓名") books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book")) # through_fields接受一個2元組('field1','field2'): # 其中field1是定義ManyToManyField的模型外鍵的名(author),field2是關聯目標模型(book)的外鍵名。 class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") class Meta: unique_together = ("author", "book")
注意:
當我們需要在第三張關系表中存儲額外的字段時,就要使用第三種方式。
但是當我們使用第三種方式創建多對多關聯關系時,就無法使用orm提供的set、add、remove、clear方法來管理多對多的關系了,需要通過第三張表的model來管理多對多關系。

to
設置要關聯的表。
to_field
設置要關聯的字段。
on_delete
同ForeignKey字段。

to 設置要關聯的表 to_field 設置要關聯的表的字段 related_name 反向操作時,使用的字段名,用於代替原反向查詢時的'表名_set'。 related_query_name 反向查詢操作時,使用的連接前綴,用於替換表名。 on_delete 當刪除關聯表中的數據時,當前表與其關聯的行的行為。

多對多的參數:
to
設置要關聯的表
related_name
同ForeignKey字段。
related_query_name
同ForeignKey字段。
through
在使用ManyToManyField字段時,Django將自動生成一張表來管理多對多的關聯關系。
但我們也可以手動創建第三張表來管理多對多關系,此時就需要通過
through來指定第三張表的表名。
through_fields
設置關聯的字段。
db_table
默認創建第三張表時,數據庫中表的名稱。

元信息 ORM對應的類里面包含另一個Meta類,而Meta類封裝了一些數據庫的信息。主要字段如下: class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") class Meta: unique_together = ("author", "book") db_table ORM在數據庫中的表名默認是 app_類名,可以通過db_table可以重寫表名。db_table = 'book_model' index_together 聯合索引。 unique_together 聯合唯一索引。 ordering 指定默認按什么字段排序。 ordering = ['pub_date',] 只有設置了該屬性,我們查詢到的結果才可以被reverse(),否則是能對排序了的結果進行反轉(order_by()方法排序過的數據)
注意事項:
- 表的名稱
app01_book_authors
,是根據 模型中的元數據自動生成的,也可以覆寫為別的名稱 id
字段是自動添加的- 對於外鍵字段,Django 會在字段名上添加"_id" 來創建數據庫中的列名
- 這個例子中的
CREATE TABLE
SQL 語句使用PostgreSQL 語法格式,要注意的是Django 會根據settings 中指定的數據庫類型來使用相應的SQL 語句。 - 外鍵字段 ForeignKey 有一個 null=True 的設置(它允許外鍵接受空值 NULL),你可以賦給它空值 None 。
關於on_delete(了解)

on_delete
當刪除關聯表中的數據時,當前表與其關聯的行的行為。
models.CASCADE
刪除關聯數據,與之關聯也刪除
models.DO_NOTHING
刪除關聯數據,引發錯誤IntegrityError
models.PROTECT
刪除關聯數據,引發錯誤ProtectedError
models.SET_NULL
刪除關聯數據,與之關聯的值設置為null(前提FK字段需要設置為可空)
models.SET_DEFAULT
刪除關聯數據,與之關聯的值設置為默認值(前提FK字段需要設置默認值)
models.SET
刪除關聯數據,
a. 與之關聯的值設置為指定值,設置:models.SET(值)
b. 與之關聯的值設置為可執行對象的返回值,設置:models.SET(可執行對象)
圖例:
二、添加表記錄
2.1 一對多(多對一):
方式1: publish_obj=Publish.objects.get(nid=1) # 拿到nid為1的出版社對象 book_obj=Book.objects.create(title="python",publishDate="2012-12-12",price=100,publish=publish_obj) #出版社對象作為值給publish,其實就是自動將publish字段變成publish_id,然后將publish_obj的id給取出來賦值給publish_id字段,注意你如果不是publish類的對象肯定會報錯的 方式2: book_obj=Book.objects.create(title="python",publishDate="2012-12-12",price=100,publish_id=1) #直接可以寫id值,注意字段屬性的寫法和上面不同,這個是publish_id=xxx,上面是publish=xxx。
2.2 多對多 :
方式一: 多對多一般在前端頁面上使用的時候是多選下拉框的樣子來給用戶選擇多個數據,這里可以讓用戶選擇多個書籍,多個作者 # 當前生成的書籍對象 book_obj=Book.objects.create(title="追風箏的人",price=200,publishDate="2012-11-12",publish_id=1) # 為書籍綁定的做作者對象 yuan=Author.objects.filter(name="yuan").first() # 在Author表中主鍵為2的紀錄,注意取的是author的model對象 egon=Author.objects.filter(name="alex").first() # 在Author表中主鍵為1的紀錄 # 有人可能會說,我們可以直接給第三張表添加數據啊,這個自動生成的第三張表你能通過models獲取到嗎,是獲取不到的,用不了的,當然如果你知道了這個表的名字,那么你通過原生sql語句可以進行書的添加,所以要通過orm間接的給第三張表添加數據,如果是你手動添加的第三張表你是可以直接給第三張表添加數據 # 綁定多對多關系,即向關系表book_authors中添加紀錄,給書添加兩個作者,下面的語法就是告訴orm給第三張表添加兩條數據 book_obj.authors.add(yuan,egon) # 將某些特定的 model 對象添加到被關聯對象集合中。 ======= book_obj.authors.add(*[]) # book_obj是書籍對象,authors是book表里面那個多對多的關系字段名稱。 # 其實orm就是先通過book_obj的authors屬性找到第三張表,然后將book_obj的id值和兩個作者對象的id值組合成兩條記錄添加到第三張表里面去
方式二: book_obj.authors.add(1,2) book_obj.authors.add(*[1,2]) #這種方式用的最多,因為一般是給用戶來選擇,用戶選擇是多選的,選完給你發送過來的就是一堆的id值
2.3 一對一:
authorDetail_obj = models.AuthorDetail.objects.get(nid=4)
# 方式一:
models.Author.objects.create(name="alex", age=14, authorDetail=authorDetail_obj) # 方式同一對多
# 方式二:
models.Author.objects.create(name="alex", age=14, authorDetail_id=4)
三、刪除記錄
刪除一條記錄: models.Book.objects.filter(id=1).delete() 多對多關系其它常用API: book_obj.authors.remove() # 將某個特定的對象從被關聯對象集合中去除。 ====== book_obj.authors.remove(*[1,2]),將多對多的關系數據刪除 book_obj.authors.clear() #清空被關聯對象集合 book_obj.authors.set() #先清空再設置 刪除示例: book_obj = models.Book.objects.filter(nid=4)[0] # book_obj.authors.remove(2) #將第三張表中的這個book_obj對象對應的那個作者id為2的那條記錄刪除 # book_obj.authors.clear() # book_obj.authors.set('2') #先清除掉所有的關系數據,然后只給這個書對象綁定這個id為2的作者,所以只剩下一條記錄 3---2,比如用戶編輯數據的時候,選擇作者發生了變化,那么需要重新選擇,所以我們就可以先清空,然后再重新綁定關系數據,注意這里寫的是字符串,數字類型不可以 book_obj.authors.set(['1',]) #這么寫也可以,但是注意列表中的元素是字符串,列表前面沒有*,之前我測試有*,感覺是版本的問題,沒事,能夠用哪個用哪個
注意:一對一和一對多的刪改和單表的刪改是一樣的,別忘了刪除表的時候,咱們是做了級聯刪除的。
四、更新記錄
# 方式一: models.Book.objects.filter(title="go").update(price=40) # 方式二: book_obj = models.Book.objects.get(id=1) # 獲取一個書籍對象 data = {'title':'xxx','price':100} # 這個書籍對象更新后的數據 models.Book.objects.filter(id=n).update(**data) #將新數據更新到原來的記錄中 book_obj.authors.set(author_list) #將數據和作者的多對多關系加上
五、基於對象的跨表查詢
5.1 一對多查詢(Publish 與 Book):
正向查詢(按字段:publish):關聯屬性字段所在的表查詢被關聯表的記錄就是正向查詢,反之就是反向查詢 # 查詢主鍵為1的書籍的出版社所在的城市 book_obj=Book.objects.filter(pk=1).first() # book_obj.publish 是主鍵為1的書籍對象關聯的出版社對象,book對象.外鍵字段名稱 print(book_obj.publish.city) 反向查詢(按表名:book_set,因為加上_set是因為反向查詢的時候,你查詢出來的可能是多條記錄的集合): publish=Publish.objects.get(name="蘋果出版社") #publish.book_set.all() : 與蘋果出版社關聯的所有書籍對象集合,寫法:小寫的表名_set.all(),得到queryset類型數據 book_list=publish.book_set.all() for book_obj in book_list: print(book_obj.title)
5.2 一對一查詢(Author與AuthorDetail)
正向查詢(按字段:authorDetail): egon=Author.objects.filter(name="egon").first() print(egon.authorDetail.telephone) egon.authorDeail就拿到了這個對象,因為一對一找到的就是一條記錄,注意寫法:作者對象.字段名,就拿到了那個關聯對象. 反向查詢(按表名:author):不需要_set,因為一對一正向反向都是找到一條記錄 # 查詢所有住址在北京的作者的姓名 authorDet=AuthorDetail.objects.filter(addr="beijing")[0] authorDet.author.name # 反向正向都只能找到一條記錄,所有不需要類名_set
5.3 多對多查詢(Author與Book)
正向查詢(按字段:authors): # python所有作者的名字以及手機號 book_obj=Book.objects.filter(title="python").first() authors=book_obj.authors.all() for author_obj in authors: print(author_obj.name,author_obj.authorDetail.telephone) 反向查詢(按表名:book_set): # 查詢egon出過的所有書籍的名字 author_obj=Author.objects.get(name="egon") book_list=author_obj.book_set.all() #與egon作者相關的所有書籍 for book_obj in book_list: print(book_obj.title)
注意: 你可以通過在 ForeignKey() 和ManyToManyField的定義中設置 related_name 的值來覆寫 FOO_set 的名稱。例如,如果 Article model 中做一下更改: publish = ForeignKey(Book, related_name='bookList') 那么接下來就會如我們看到這般: # 查詢 人民出版社出版過的所有書籍 publish=Publish.objects.get(name="人民出版社") book_list=publish.bookList.all() # 與人民出版社關聯的所有書籍對象集合
補充一點: 那就是form表單里面的button按鈕和form表單外面的button按鈕的區別,form表單里面的button按鈕其實和input type='submit'的標簽是有同樣的效果的,都能夠提交form表單的數據,但是如果放在form表單外面的button按鈕,那就只是個普通的按鈕了。<button>提交</button>,還有一點,input type='submit'按鈕放到form表單外面那就成了一個普通的按鈕。
六、基於雙下划線的跨表查詢(基於join實現的)
Django 還提供了一種直觀而高效的方式在查詢(lookups)中表示關聯關系,它能自動確認 SQL JOIN 聯系。要做跨關系查詢,就使用兩個下划線來鏈接模型(model)間關聯字段的名稱,直到最終鏈接到你想要的model 為止。
''' 基於雙下划線的查詢就一句話:正向查詢按字段,反向查詢按表名小寫用來告訴ORM引擎join哪張表,一對一、一對多、多對多都是一個寫法,注意,我們寫orm查詢的時候,哪個表在前哪個表在后都沒問題,因為走的是join連表操作。 ''' 一對多查詢: # 練習: 查詢蘋果出版社出版過的所有書籍的名字與價格(一對多) # 正向查詢 按字段:publish queryResult=Book.objects .filter(publish__name="蘋果出版社") #通過__告訴orm將book表和publish表進行join,然后找到所有記錄中publish.name='蘋果出版社'的記錄(注意publish是屬性名稱),然后select book.title,book.price的字段值 .values_list("title","price") #values或者values_list # 反向查詢 按表名:book queryResult=Publish.objects.filter(name="蘋果出版社") .values_list("book__title","book__price") 多對多查詢: # 練習: 查詢yuan出過的所有書籍的名字(多對多) # 正向查詢 按字段:authors: queryResult=Book.objects.filter(authors__name="yuan") .values_list("title") # 反向查詢 按表名:book queryResult=Author.objects.filter(name="yuan") .values_list("book__title","book__price") 一對一查詢: # 查詢yuan的手機號 # 正向查詢 ret=Author.objects.filter(name="yuan").values("authordetail__telephone") # 反向查詢 ret=AuthorDetail.objects.filter(author__name="yuan").values("telephone")
進階練習(連續跨表)
# 練習: 查詢人民出版社出版過的所有書籍的名字以及作者的姓名 # 正向查詢 queryResult=Book.objects .filter(publish__name="人民出版社") .values_list("title","authors__name") # 反向查詢 queryResult=Publish.objects .filter(name="人民出版社") .values_list("book__title","book__authors__age","book__authors__name") # 練習: 手機號以151開頭的作者出版過的所有書籍名稱以及出版社名稱 # 方式1: queryResult=Book.objects .filter(authors__authorDetail__telephone__regex="151") # 正則表達式匹配151的字符串 .values_list("title","publish__name") # 方式2: ret=Author.objects .filter(authordetail__telephone__startswith="151") # 以151開頭的字符串 .values("book__title","book__publish__name")
related_name的應用:
反向查詢時,如果定義了related_name ,則用related_name替換 表名,例如: publish = ForeignKey(Blog, related_name='bookList') # modeles.py # 練習: 查詢人民出版社出版過的所有書籍的名字與價格(一對多) # 反向查詢 不再按表名:book,而是related_name:bookList queryResult=Publish.objects .filter(name="人民出版社") .values_list("bookList__title","bookList__price")
七、聚合查詢(aggregate)
aggregate(*args, **kwargs) # 計算所有圖書的平均價格 >>> from django.db.models import Avg >>> Book.objects.all().aggregate(Avg('price')) #或者給它起名字:aggretate(a=Avg('price')) {'price__avg': 34.35} aggregate()是QuerySet 的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。鍵的名稱是聚合值的標識符,值是計算出來的聚合值。鍵的名稱是按照字段和聚合函數的名稱自動生成出來的。如果你想要為聚合值指定一個名稱,可以向聚合子句提供它。 >>> Book.objects.aggregate(average_price=Avg('price')) {'average_price': 34.35} 如果你希望生成不止一個聚合,你可以向aggregate()子句中添加另一個參數。所以,如果你也想知道所有圖書價格的最大值和最小值,可以這樣查詢: >>> from django.db.models import Avg, Max, Min >>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) #count('id'),count(1)也可以統計個數,Book.objects.all().aggregete和Book.objects.aggregate(),都可以 {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
八、F查詢
在上面所有的例子中,我們構造的過濾器都只是將字段值與某個常量做比較。如果我們要對兩個字段的值做比較,那該怎么做呢?我們在book表里面加上兩個字段:評論數:commentNum,收藏數:KeepNum Django 提供 F() 來做這樣的比較。F() 的實例可以在查詢中引用字段,來比較同一個 model 實例中兩個不同字段的值。 # 查詢評論數大於收藏數的書籍 from django.db.models import F Book.objects.filter(commentNum__lt=F('keepNum')) Django 支持 F() 對象之間以及 F() 對象和常數之間的加減乘除和取模的操作。 # 查詢評論數大於收藏數2倍的書籍 Book.objects.filter(commentNum__lt=F('keepNum')*2) 修改操作也可以使用F函數,比如將每一本書的價格提高30元: Book.objects.all().update(price=F("price")+30)
九、Q查詢
filter() 等方法中的關鍵字參數查詢都是一起進行“AND” 的。 如果你需要執行更復雜的查詢(例如OR 語句),你可以使用Q 對象。 from django.db.models import Q Q(title__startswith='Py') Q 對象可以使用&(與) 、|(或)、~(非) 操作符組合起來。當一個操作符在兩個Q 對象上使用時,它產生一個新的Q 對象。 bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon")) 等同於下面的SQL WHERE 子句: WHERE name ="yuan" OR name ="egon"
你可以組合& 和| 操作符以及使用括號進行分組來編寫任意復雜的Q 對象。同時,Q 對象可以使用~ 操作符取反,這允許組合正常的查詢和取反(NOT) 查詢: bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title") bookList=Book.objects.filter(Q(Q(authors__name="yuan") & ~Q(publishDate__year=2017))&Q(id__gt=6)).values_list("title") #可以進行Q嵌套,多層Q嵌套等,其實工作中比較常用 查詢函數可以混合使用Q 對象和關鍵字參數。所有提供給查詢函數的參數(關鍵字參數或Q 對象)都將"AND”在一起。但是,如果出現Q 對象,它必須位於所有關鍵字參數的前面。例如: bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017), title__icontains="python" # ,也是and的關系,但是Q必須寫在前面 )
十、分組查詢(annotate)
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)
###################################--單表分組查詢--####################################################### 查詢每一個部門名稱以及對應的員工數 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") #注意:annotate里面必須寫個聚合函數,不然沒有意義,並且必須有個別名=,別名隨便寫,但是必須有,用哪個字段分組,values里面就寫哪個字段,annotate其實就是對分組結果的統計,統計你需要什么。 ''' select dep,count('id') as c from emp grouby dep; #原生sql語句中的as c,不是必須有的 ''' ###################################--多表分組查詢--########################### 多表分組查詢: 查詢每一個部門名稱以及對應的員工數 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 人事部 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") ret = models.Emp.objects.values('dep_id','name').annotate(a=Count(1)) # dep_id 與 name 兩個字段重復,才算重復,才能算一組 ''' SELECT `app01_emp`.`dep_id`, `app01_emp`.`name`, COUNT(1) AS `a` FROM `app01_emp` GROUP BY `app01_emp`.`dep_id`, `app01_emp`.`name` ''' #<QuerySet [{'dep_id': 1, 'name': 'alex', 'a': 1}, {'dep_id': 2, 'name': 'egon', 'a': 1}, {'dep_id': 2, 'name': 'wen', 'a': 1}]>,注意,這里如果你寫了其他字段,那么只有這兩個字段重復,才算一組,合並到一起來統計個數
總結 :跨表分組查詢本質就是將關聯表join成一張表,再按單表的思路進行分組查詢,,既然是join連表,就可以使用咱們的雙下划線進行連表了。
#單表: #查詢每一個部門的id以及對應員工的平均薪水 ret = models.Emp.objects.values('dep_id').annotate(s=Avg('salary')) #查詢每個部門的id以及對對應的員工的最大年齡 ret = models.Emp.objects.values('dep_id').annotate(a=Max('age')) #Emp表示表,values中的字段表示按照哪個字段group by,annotate里面是顯示分組統計的是什么 #連表: # 查詢每個部門的名稱以及對應的員工個數和員工最大年齡 ret = models.Emp.objects.values('dep__name').annotate(a=Count('id'),b=Max('age')) #注意,正向與反向的結果可能不同,如果反向查的時候,有的部門還沒有員工,那么他的數據也會被統計出來,只不過值為0,但是正向查的話只能統計出來有員工的部門的相關數據,因為通過你是員工找部門,而不是通過部門找員工,結果集里面的數據個數不同,但是你想要的統計結果是一樣的 #<QuerySet [{'a': 1, 'dep__name': '銷售部', 'b': 12}, {'a': 3, 'dep__name': '人事部', 'b': 22}]> #使用雙下划線進行連表,然后按照部門名稱進行分組,然后統計員工個數和最大年齡,最后結果里面顯示的是部門名稱、個數、最大年齡。 #注意:如果values里面有多個字段的情況: ret = models.Emp.objects.values('dep__name','age').annotate(a=Count('id'),b=Max('age')) #是按照values里面的兩個字段進行分組,兩個字段同時相同才算是一組,看下面的sql語句 ''' SELECT `app01_dep`.`name`, `app01_emp`.`age`, COUNT(`app01_emp`.`id`) AS `a`, MAX(`app01_emp`.`age`) AS `b` FROM `app01_emp` INNER JOIN `app01_dep` ON (`app01_emp`.`dep_id` = `app01_dep`.`id`) GROUP BY `app01_dep`.`name`, `app01_emp`.`age`; '''
查詢練習: (1) 練習:統計每一個出版社的最便宜的書 publishList=Publish.objects.annotate(MinPrice=Min("book__price")) #如果沒有使用objects后面values或者values_list,得到的結果是queryset類型,里面是Publish的model對象,並且是對所有記 錄進行的統計,統計的Minprice也成了這些model對象里面的一個屬性,這種連表分組統計的寫法最常用,思路也比較清晰 for publish_obj in publishList: print(publish_obj.name,publish_obj.MinPrice) annotate的返回值是querySet,如果不想遍歷對象,可以用上valuelist: queryResult= Publish.objects .annotate(MinPrice=Min("book__price")) .values_list("name","MinPrice") print(queryResult) (2) 練習:統計每一本書的作者個數 ret=Book.objects.annotate(authorsNum=Count('authors__name')) ret=models.Book.objects.annotate(authorsNum=Count('authors__name')).values('title','authorsNum') #注意寫法,values里面寫的個數的別名 ret=models.Book.objects.annotate(a=Count('author__name')).filter(a__gt=2).values('title','a') #還有這種寫法,看看你能不能明白這是在做什么 (3) 統計每一本以py開頭的書籍的作者個數: queryResult=Book.objects .filter(title__startswith="Py") .annotate(num_authors=Count('authors')) #連接第三張表再連接author表,where title regexp '^Py' 然后按照連表后的大表中的book表的title字段進行分組,並且統計對應作者的個數 (4) 統計不止一個作者的圖書: queryResult=Book.objects .annotate(num_authors=Count('authors')) .filter(num_authors__gt=1) #filter也是也可以是querset來調用 (5) 根據一本圖書作者數量的多少對查詢集 QuerySet進行排序: Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors') (6) 查詢各個作者出的書的總價格: # 按author表的所有字段 group byqueryResult=Author.objects .annotate(SumPrice=Sum("book__price")) .values_list("name","SumPrice") print(queryResult)
補充:
# 多表查詢:查詢出版社的名稱和每個出版過的書籍的最高價
# 方式一
ret = models.Book.objects.values("publish__name").annotate(m=Max("price"))
print(ret)
# 方式二
ret = models.Publish.objects.values("name").annotate(m=Max("book__price"))
print(ret)
# 方式三
ret = models.Publish.objects.annotate(m=Max("book__price")).values("name", "m")
print(ret)
綜合查詢練習題:
#1 查詢每個作者的姓名以及出版的書的最高價格 ret = models.Author.objects.values('name').annotate(max_price=Max('book__price')) print(ret) #注意:values寫在annotate前面是作為分組依據用的,並且返回給你的值就是這個values里面的字段(name)和分組統計的結果字段數據(max_price) # ret = models.Author.objects.annotate(max_price=Max('book__price')).values('name','max_price')#這種寫法是按照Author表的id字段進行分組,返回給你的是這個表的所有model對象,這個對象里面包含着max_price這個屬性,后面寫values方法是獲取的這些對象的屬性的值,當然,可以加雙下划線來連表獲取其他關聯表的數據,但是獲取的其他關聯表數據是你的這些model對象對應的數據,而關聯獲取的數據可能不是你想要的最大值對應的那些數據 # 2 查詢作者id大於2作者的姓名以及出版的書的最高價格 ret = models.Author.objects.filter(id__gt=2).annotate(max_price=Max('book__price')).values('name','max_price')#記着,這個values取得是前面調用這個方法的表的所有字段值以及max_pirce的值,這也是為什么我們取關聯數據的時候要加雙划線的原因 print(ret) #3 查詢作者id大於2或者作者年齡大於等於20歲的女作者的姓名以及出版的書的最高價格 # ret = models.Author.objects.filter(Q(id__gt=2)|Q(age__gte=20),sex='female').annotate(max_price=Max('book__price')).values('name','max_price') #4 查詢每個作者出版的書的最高價格 的平均值 # ret = models.Author.objects.values('id').annotate(max_price=Max('book__price')).aggregate(Avg('max_price')) #{'max_price__avg': 555.0} 注意,aggregate是queryset的終止句,得到的是字典 # ret = models.Author.objects.annotate(max_price=Max('book__price')).aggregate(Avg('max_price')) #{'max_price__avg': 555.0} 注意,aggregate是queryset的終止句,得到的是字典 #5 每個作者出版的所有書的價格以及最高價格的那本書的名稱(通過orm玩起來就是個死題,需要用原生sql) ''' select title,price from (select app01_author.id,app01_book.title,app01_book.price from app01_author INNER JOIN app01_book_authors on app01_author.id= app01_book_authors.author_id INNER JOIN app01_book on app01_book.id= app01_book_authors.book_id ORDER BY app01_book.price desc) as b GROUP BY id ''' print(ret)
十一、Python腳本中調用Django環境(django外部腳本使用models)
如果你想通過自己創建的python文件在django項目中使用django的models,那么就需要調用django的環境:
import os if __name__ == '__main__': os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings") import django django.setup() from app01 import models #引入也要寫在上面三句之后 books = models.Book.objects.all() print(books)
十二、ORM執行原生sql語句
在模型查詢API不夠用的情況下,我們還可以使用原始的SQL語句進行查詢。
Django 提供兩種方法使用原始SQL進行查詢:一種是使用raw()方法,進行原始SQL查詢並返回模型實例;另一種是完全避開模型層,直接執行自定義的SQL語句。
執行原生查詢: raw()管理器方法用於原始的SQL查詢,並返回模型的實例: 注意:raw()語法查詢必須包含主鍵。 這個方法執行原始的SQL查詢,並返回一個django.db.models.query.RawQuerySet 實例。 這個RawQuerySet 實例可以像一般的QuerySet那樣,通過迭代來提供對象實例。 舉個例子: class Person(models.Model): first_name = models.CharField(...) last_name = models.CharField(...) birth_date = models.DateField(...) 可以像下面這樣執行原生SQL語句 >>> for p in Person.objects.raw('SELECT * FROM myapp_person'): ... print(p) raw()查詢可以查詢其他表的數據。 舉個例子: ret = models.Student.objects.raw('select id, tname as hehe from app02_teacher') for i in ret: print(i.id, i.hehe) raw()方法自動將查詢字段映射到模型字段。還可以通過translations參數指定一個把查詢的字段名和ORM對象實例的字段名互相對應的字典 d = {'tname': 'haha'} ret = models.Student.objects.raw('select * from app02_teacher', translations=d) for i in ret: print(i.id, i.sname, i.haha) 原生SQL還可以使用參數,注意不要自己使用字符串格式化拼接SQL語句,防止SQL注入! d = {'tname': 'haha'} ret = models.Student.objects.raw('select * from app02_teacher where id > %s', translations=d, params=[1,]) for i in ret: print(i.id, i.sname, i.haha)
直接執行自定義SQL 有時候raw()方法並不十分好用,很多情況下我們不需要將查詢結果映射成模型,或者我們需要執行DELETE、 INSERT以及UPDATE操作。在這些情況下,我們可以直接訪問數據庫,完全避開模型層。 我們可以直接從django提供的接口中獲取數據庫連接,然后像使用pymysql模塊一樣操作數據庫。 from django.db import connection, connections cursor = connection.cursor() # cursor = connections['default'].cursor() cursor.execute("""SELECT * from auth_user where id = %s""", [1]) ret = cursor.fetchone()