Django基礎五之django模型層(二)多表操做

一 建立模型

表和表之間的關係前端

  一對1、多對1、多對多 ,用book表和publish表本身來想一想關係,想一想裏面的操做,加外鍵約束和不加外鍵約束的區別,一對一的外鍵約束是在一對多的約束上加上惟一約束。python

  實例:咱們來假定下面這些概念,字段和關係mysql

  做者模型:一個做者有姓名和年齡。git

  做者詳細模型:把做者的詳情放到詳情表,包含生日,手機號,家庭住址等信息。做者詳情模型和做者模型之間是一對一的關係(one-to-one)sql

  出版商模型:出版商有名稱,所在城市以及email。數據庫

  書籍模型: 書籍有書名和出版日期,一本書可能會有多個做者,一個做者也能夠寫多本書,因此做者和書籍的關係就是多對多的關聯關係(many-to-many);一本書只應該由一個出版商出版,因此出版商和書籍是一對多關聯關係(one-to-many)。django

  模型創建以下:app

 1 from django.db import models
 2 
 3 # Create your models here.
 4 
 5 
 6 class Author(models.Model): #比較經常使用的信息放到這個表裏面
 7     nid = models.AutoField(primary_key=True)
 8     name=models.CharField( max_length=32)
 9     age=models.IntegerField()
10 
11     # 與AuthorDetail創建一對一的關係,一對一的這個關係字段寫在兩個表的任意一個表裏面均可以
12     authorDetail=models.OneToOneField(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE) #就是foreignkey+unique,只不過不須要咱們本身來寫參數了,而且orm會自動幫你給這個字段名字拼上一個_id,數據庫中字段名稱爲authorDetail_id
13 
14 class AuthorDetail(models.Model):#不經常使用的放到這個表裏面
15 
16     nid = models.AutoField(primary_key=True)
17     birthday=models.DateField()
18     telephone=models.BigIntegerField()
19     addr=models.CharField( max_length=64)
20 
21 class Publish(models.Model):
22     nid = models.AutoField(primary_key=True)
23     name=models.CharField( max_length=32)
24     city=models.CharField( max_length=32)
25     email=models.EmailField()
26 
27 #多對多的表關係,咱們學mysql的時候是怎麼創建的,是否是手動建立一個第三張表,而後寫上兩個字段,每一個字段外鍵關聯到另外兩張多對多關係的表,orm的manytomany自動幫咱們建立第三張表,兩種方式創建關係均可以,之後的學習咱們暫時用orm自動建立的第三張表,由於手動建立的第三張表咱們進行orm操做的時候,不少關於多對多關係的表之間的orm語句方法沒法使用
28 #若是你想刪除某張表,你只須要將這個表註銷掉,而後執行那兩個數據庫同步指令就能夠了,自動就刪除了。
29 class Book(models.Model):
30 
31     nid = models.AutoField(primary_key=True)
32     title = models.CharField( max_length=32)
33     publishDate=models.DateField()
34     price=models.DecimalField(max_digits=5,decimal_places=2)
35 
36     # 與Publish創建一對多的關係,外鍵字段創建在多的一方,字段publish若是是外鍵字段,那麼它自動是int類型
37     publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE) #foreignkey裏面能夠加不少的參數,都是須要我們學習的,慢慢來,to指向表,to_field指向你關聯的字段,不寫這個,默認會自動關聯主鍵字段,on_delete級聯刪除
38    字段名稱不須要寫成publish_id,orm在翻譯foreignkey的時候會自動給你這個字段拼上一個_id,這個字段名稱在數據庫裏面就自動變成了publish_id
39     # 與Author表創建多對多的關係,ManyToManyField能夠建在兩個模型中的任意一個,自動建立第三張表,而且注意一點,你查看book表的時候,你看不到這個字段,由於這個字段就是建立第三張表的意思,不是建立字段的意思,因此只能說這個book類裏面有authors這個字段屬性
40     authors=models.ManyToManyField(to='Author',) #注意無論是一對多仍是多對多,寫to這個參數的時候,最後後面的值是個字符串,否則你就須要將你要關聯的那個表放到這個表的上面

關於多對多表的三種建立方式(目前你先做爲了解)ide

方式一:自行建立第三張表

 1 class Book(models.Model):
 2     title = models.CharField(max_length=32, verbose_name="書名")
 3 
 4 
 5 class Author(models.Model):
 6     name = models.CharField(max_length=32, verbose_name="做者姓名")
 7 
 8 
 9 # 本身建立第三張表,分別經過外鍵關聯書和做者
10 class Author2Book(models.Model):
11     author = models.ForeignKey(to="Author")
12     book = models.ForeignKey(to="Book")
13 
14     class Meta:
15         unique_together = ("author", "book")

方式二:經過ManyToManyField自動建立第三張表

1 class Book(models.Model):
2     title = models.CharField(max_length=32, verbose_name="書名")
3 
4 
5 # 經過ORM自帶的ManyToManyField自動建立第三張表
6 class Author(models.Model):
7     name = models.CharField(max_length=32, verbose_name="做者姓名")
8     books = models.ManyToManyField(to="Book", related_name="authors")  #自動生成的第三張表咱們是沒有辦法添加其餘字段的

方式三:設置ManyTomanyField並指定自行建立的第三張表(稱爲中介模型)

 1 class Book(models.Model):
 2     title = models.CharField(max_length=32, verbose_name="書名")
 3 
 4 
 5 # 本身建立第三張表,並經過ManyToManyField指定關聯
 6 class Author(models.Model):
 7     name = models.CharField(max_length=32, verbose_name="做者姓名")
 8     books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book"))
 9     # through_fields接受一個2元組('field1','field2'):
10     # 其中field1是定義ManyToManyField的模型外鍵的名(author),field2是關聯目標模型(book)的外鍵名。
11 
12 
13 class Author2Book(models.Model):
14     author = models.ForeignKey(to="Author")
15     book = models.ForeignKey(to="Book")
16     #能夠擴展其餘的字段了
17     class Meta:
18         unique_together = ("author", "book")

注意:函數

  當咱們須要在第三張關係表中存儲額外的字段時,就要使用第三種方式,第三種方式仍是可使用多對多關聯關係操做的接口(all、add、clear等等)

  當咱們使用第一種方式建立多對多關聯關係時,就沒法使用orm提供的set、add、remove、clear方法來管理多對多的關係了。

1 to
2     設置要關聯的表。
3 
4 to_field
5     設置要關聯的字段。
6     
7 on_delete
8     同ForeignKey字段。
建立一對一關係字段時的一些參數
 1 to
 2     設置要關聯的表
 3 
 4 to_field
 5     設置要關聯的表的字段
 6 
 7 related_name
 8     反向操做時,使用的字段名,用於代替原反向查詢時的'表名_set' 9 related_query_name
10     反向查詢操做時,使用的鏈接前綴,用於替換表名。
11 
12 on_delete
13     當刪除關聯表中的數據時,當前表與其關聯的行的行爲。
建立一對多關係字段時的一些參數
 1 多對多的參數:
 2     to
 3         設置要關聯的表
 4 
 5     related_name
 6         同ForeignKey字段。
 7 
 8     related_query_name
 9         同ForeignKey字段。
10     through
11         在使用ManyToManyField字段時,Django將自動生成一張表來管理多對多的關聯關係。
12 
13         但咱們也能夠手動建立第三張表來管理多對多關係,此時就須要經過        
14     through來指定第三張表的表名。
15 
16     through_fields
17         設置關聯的字段。
18 
19     db_table
20         默認建立第三張表時,數據庫中表的名稱。            
建立多對多字段時的一些參數
 1 元信息
 2     ORM對應的類裏面包含另外一個Meta類,而Meta類封裝了一些數據庫的信息。主要字段以下:
 3 class Author2Book(models.Model):
 4     author = models.ForeignKey(to="Author")
 5     book = models.ForeignKey(to="Book")
 6     class Meta:
 7         unique_together = ("author", "book")
 8 
 9 db_table
10     ORM在數據庫中的表名默認是 app_類名,能夠經過db_table能夠重寫表名。db_table = 'book_model'
11 
12 index_together
13     聯合索引。
14 
15 unique_together
16     聯合惟一索引。
17 
18 ordering
19     指定默認按什麼字段排序。
20     ordering = ['pub_date',]
21     只有設置了該屬性,咱們查詢到的結果才能夠被reverse(),不然是能對排序了的結果進行反轉(order_by()方法排序過的數據)
建立表時的一些元信息設置

  獲取元信息,能夠經過model對象._meta.verbose_name等獲取本身經過verbose_name指定的表名,model對象._meta.model_name獲取小寫的表名,還有model對象.app_label能夠獲取這個對象的app應用名等等操做。例如:book_obj = models.Book.objects.get(id=1),book_obj._meta.model_name。

關於db_column和verbose_name 

  1.指定字段名: 在定義字段的時候,增長參數db_column=’real_field’; 

  2.指定表名: 在model的class中,添加Meta類,在Meta類中指定表名db_table 

  例如在某個models.py文件中,有一個類叫Info:

 1 class Info(models.Model):  
 2     ''''' 
 3             信息統計 
 4     '''  
 5     app_id = models.ForeignKey(App)  
 6     app_name = models.CharField(verbose_name='應用名',  max_length=32, db_column='app_name2')  
 7   
 8     class Meta:  
 9         db_table = 'info'  
10         verbose_name = '信息統計'  
11         verbose_name_plural = '信息統計'

  其中db_column指定了對應的字段名,db_table指定了對應的代表; 

  若是不這樣指定,字段名默認爲app_name, 而代表默認爲app名+類名: [app_name]_info.

  verbose_name指定在admin管理界面中顯示中文;verbose_name表示單數形式的顯示,verbose_name_plural表示複數形式的顯示;中文的單數和複數通常不做區別。

 

  建立完這個表,咱們本身能夠經過navicat工具來看看數據庫裏面的那些表,出版社這個表裏面沒有任何的關係字段,這種單表的數據,咱們能夠先添加幾條數據,在進行下面的增刪改查的操做。

  生成表以下:

    

    

    

    

    

   

  注意事項:

  •  表的名稱myapp_modelName,是根據 模型中的元數據自動生成的,也能夠覆寫爲別的名稱  
  •  id 字段是自動添加的
  •  對於外鍵字段,Django 會在字段名上添加"_id" 來建立數據庫中的列名
  •  這個例子中的CREATE TABLE SQL 語句使用PostgreSQL 語法格式,要注意的是Django 會根據settings 中指定的數據庫類型來使用相應的SQL 語句。
  •  定義好模型以後,你須要告訴Django _使用_這些模型。你要作的就是修改配置文件中的INSTALL_APPSZ中設置,在其中添加models.py所在應用的名稱。
  • 外鍵字段 ForeignKey 有一個 null=True 的設置(它容許外鍵接受空值 NULL),你能夠賦給它空值 None 。

   我們的表裏麪包含了一對1、一對多、多對多的關係,咱們基於這幾個表來練習,未來不管有多少張表,都逃脫不了這三個關係,操做起來都是同樣的。

    關於on_delete(瞭解)

 1 on_delete
 2 當刪除關聯表中的數據時,當前表與其關聯的行的行爲。
 3 
 4 models.CASCADE
 5 刪除關聯數據,與之關聯也刪除
 6 
 7 
 8 models.DO_NOTHING
 9 刪除關聯數據,引起錯誤IntegrityError
10 
11 
12 models.PROTECT
13 刪除關聯數據,引起錯誤ProtectedError
14 
15 
16 models.SET_NULL
17 刪除關聯數據,與之關聯的值設置爲null(前提FK字段須要設置爲可空)
18 
19 
20 models.SET_DEFAULT
21 刪除關聯數據,與之關聯的值設置爲默認值(前提FK字段須要設置默認值)
22 
23 
24 models.SET
25 
26 刪除關聯數據,
27 a. 與之關聯的值設置爲指定值,設置:models.SET(值)
28 b. 與之關聯的值設置爲可執行對象的返回值,設置:models.SET(可執行對象)
關於on_delete參數
1 ForeignKey的db_contraint參數
2 
3 關係和約束你們要搞清楚,我不加外鍵能不能表示兩個表之間的關係啊,固然能夠
4 
5 可是咱們就不能使用ORM外鍵相關的方法了,因此咱們單純的將外鍵換成一個其餘字段類型,只是單純的存着另一個關聯表的主鍵值是不能使用ORM外鍵方法的。
6 
7 #db_constraint=False只加二者的關係,沒有強制約束的效果,而且ORM外鍵相關的接口(方法)還能使用,因此若是未來公司讓你創建外鍵,而且不能有強制的約束關係,那麼就能夠將這個參數改成False
8     customer = models.ForeignKey(verbose_name='關聯客戶', to='Customer',db_constraint=False)
ForeignKey的db_contraint參數

二 添加表記錄

操做前先簡單的錄入一些數據:仍是create和save兩個方法,和單表的區別就是看看怎麼添加關聯字段的數據

  publish表:

    

  author表:

    

  authordetail表:

    

  

  一對多

1 方式1:
2    publish_obj=Publish.objects.get(nid=1) #拿到nid爲1的出版社對象
3    book_obj=Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=100,publish=publish_obj) #出版社對象做爲值給publish,其實就是自動將publish字段變成publish_id,而後將publish_obj的id給取出來賦值給publish_id字段,注意你若是不是publish類的對象確定會報錯的,別亂昂
4   
5 方式2:
6    book_obj=Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=100,publish_id=1)  #直接能夠寫id值,注意字段屬性的寫法和上面不一樣,這個是publish_id=xxx,上面是publish=xxx。

 

  

  核心:book_obj.publish與book_obj.publish_id是什麼? 

   

  多對多

 1 方式一: 多對多通常在前端頁面上使用的時候是多選下拉框的樣子來給用戶選擇多個數據,這裏可讓用戶選擇多個書籍,多個做者
 2   # 當前生成的書籍對象
 3     book_obj=Book.objects.create(title="追風箏的人",price=200,publishDate="2012-11-12",publish_id=1)
 4     # 爲書籍綁定的作做者對象
 5     yuan=Author.objects.filter(name="yuan").first() # 在Author表中主鍵爲2的紀錄,注意取的是author的model對象
 6     egon=Author.objects.filter(name="alex").first() # 在Author表中主鍵爲1的紀錄
 7   #有人可能會說,咱們能夠直接給第三張表添加數據啊,這個自動生成的第三張表你能經過models獲取到嗎,是獲取不到的,用不了的,固然若是你知道了這個表的名字,那麼你經過原生sql語句能夠進行書的添加,因此要經過orm間接的給第三張表添加數據,若是是你手動添加的第三張表你是能夠直接給第三張表添加數據
 8     # 綁定多對多關係,即向關係表book_authors中添加紀錄,給書添加兩個做者,下面的語法就是告訴orm給第三張表添加兩條數據
 9     book_obj.authors.add(yuan,egon)    #  將某些特定的 model 對象添加到被關聯對象集合中。   =======    book_obj.authors.add(*[])
10     #book_obj是書籍對象,authors是book表裏面那個多對多的關係字段名稱。
11     #其實orm就是先經過book_obj的authors屬性找到第三張表,而後將book_obj的id值和兩個做者對象的id值組合成兩條記錄添加到第三張表裏面去
12   方式二
13     book_obj.authors.add(1,2)
14     book_obj.authors.add(*[1,2]) #這種方式用的最多,由於通常是給用戶來選擇,用戶選擇是多選的,選完給你發送過來的就是一堆的id值

 

  數據庫表紀錄生成以下:

  book表 

    

  book_authors表

    

  核心:book_obj.authors.all()是什麼?

 

  多對多關係其它經常使用API:

1 book_obj.authors.remove()      # 將某個特定的對象從被關聯對象集合中去除。    ======   book_obj.authors.remove(*[1,2]),將多對多的關係數據刪除
2 book_obj.authors.clear()       #清空被關聯對象集合
3 book_obj.authors.set()         #先清空再設置   ===== 

   刪除示例:

1   book_obj = models.Book.objects.filter(nid=4)[0]
2     # book_obj.authors.remove(2) #將第三張表中的這個book_obj對象對應的那個做者id爲2的那條記錄刪除
3     # book_obj.authors.clear()
4     # book_obj.authors.set('2') #先清除掉全部的關係數據,而後只給這個書對象綁定這個id爲2的做者,因此只剩下一條記錄  3---2,好比用戶編輯數據的時候,選擇做者發生了變化,那麼須要從新選擇,因此咱們就能夠先清空,而後再從新綁定關係數據,注意這裏寫的是字符串,數字類型不能夠
5     book_obj.authors.set(['1',]) #這麼寫也能夠,可是注意列表中的元素是字符串,列表前面沒有*,以前我測試有*,感受是版本的問題,沒事,可以用哪一個用哪一個

   

  一對一和一對多的刪改和單表的刪改是同樣的,別忘了刪除表的時候,我們是作了級聯刪除的。

1 更新:
2 book_obj = models.Book.objects.get(id=1) #獲取一個書籍對象
3 data = {'title':'xxx','price':100} #這個書籍對象更新後的數據
4 models.Book.objects.filter(id=n).update(**data) #將新數據更新到原來的記錄中
5 book_obj.authors.set(author_list) #將數據和做者的多對多關係加上
6 
7 刪除:
8 models.Book.objects.filter(id=1).delete()

   接下來要學的查詢就是咱的重頭戲了,比較複雜。

三 基於對象的跨表查詢

 跨表查詢是分組查詢的基礎,F和Q查詢是最簡單的,因此認真學習跨表查詢

  一對多查詢(Publish 與 Book)

    

    正向查詢(按字段:publish):關聯屬性字段所在的表查詢被關聯表的記錄就是正向查詢,反之就是反向查詢

1 # 查詢主鍵爲1的書籍的出版社所在的城市
2 book_obj=Book.objects.filter(pk=1).first()
3 # book_obj.publish 是主鍵爲1的書籍對象關聯的出版社對象,book對象.外鍵字段名稱
4 print(book_obj.publish.city)  

    反向查詢(按表名:book_set,由於加上_set是由於反向查詢的時候,你查詢出來的多是多條記錄的集合):

1 publish=Publish.objects.get(name="蘋果出版社")
2 #publish.book_set.all() : 與蘋果出版社關聯的全部書籍對象集合,寫法:小寫的表名_set.all(),獲得queryset類型數據
3 book_list=publish.book_set.all()    
4 for book_obj in book_list:
5        print(book_obj.title)

一對一查詢(Author與AuthorDetail) 

  

  正向查詢(按字段:authorDetail):

1 egon=Author.objects.filter(name="egon").first()
2 print(egon.authorDetail.telephone) egon.authorDeail就拿到了這個對象,由於一對一找到的就是一條記錄,注意寫法:做者對象.字段名,就拿到了那個關聯對象

    反向查詢(按表名:author):不須要_set,由於一對一正向反向都是找到一條記錄

1 # 查詢全部住址在北京的做者的姓名
2  
3 authorDet=AuthorDetail.objects.filter(addr="beijing")[0]
4 authorDet.author.name

 多對多查詢(Author與Book)

  

  正向查詢(按字段:authors):

1 # 金瓶眉全部做者的名字以及手機號
2  
3 book_obj=Book.objects.filter(title="金瓶眉").first()
4 authors=book_obj.authors.all()
5 for author_obj in authors:
6      print(author_obj.name,author_obj.authorDetail.telephone)

    反向查詢(按表名:book_set):

1 # 查詢egon出過的全部書籍的名字
2  
3     author_obj=Author.objects.get(name="egon")
4     book_list=author_obj.book_set.all()        #與egon做者相關的全部書籍
5     for book_obj in book_list:
6         print(book_obj.title)

    注意:

      你能夠經過在 ForeignKey() 和ManyToManyField的定義中設置 related_name 的值來覆寫 FOO_set 的名稱。例如,若是 Article model 中作一下更改:

1 publish = ForeignKey(Book, related_name='bookList')

    那麼接下來就會如咱們看到這般:

1 # 查詢 人民出版社出版過的全部書籍
2  
3 publish=Publish.objects.get(name="人民出版社")
4 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 爲止。

1 '''
2     基於雙下劃線的查詢就一句話:正向查詢按字段,反向查詢按表名小寫用來告訴ORM引擎join哪張表,一對1、一對多、多對多都是一個寫法,注意,咱們寫orm查詢的時候,哪一個表在前哪一個表在後都沒問題,由於走的是join連表操做。
3 '''

一對多查詢

 1 # 練習:  查詢蘋果出版社出版過的全部書籍的名字與價格(一對多) 
 2 
 3     # 正向查詢 按字段:publish
 4 
 5     queryResult=Book.objects
 6             .filter(publish__name="蘋果出版社")  #經過__告訴orm將book表和publish表進行join,而後找到全部記錄中publish.name='蘋果出版社'的記錄(注意publish是屬性名稱),而後select book.title,book.price的字段值
 7             .values_list("title","price") #values或者values_list
 8 
 9     # 反向查詢 按表名:book
10 
11     queryResult=Publish.objects
12               .filter(name="蘋果出版社")
13               .values_list("book__title","book__price")

多對多查詢  

 1 # 練習: 查詢yuan出過的全部書籍的名字(多對多)
 2 
 3     # 正向查詢 按字段:authors:
 4     queryResult=Book.objects
 5             .filter(authors__name="yuan")
 6             .values_list("title")
 7 
 8     # 反向查詢 按表名:book
 9     queryResult=Author.objects
10               .filter(name="yuan")
11               .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")

進階練習(連續跨表)

 1 # 練習: 查詢人民出版社出版過的全部書籍的名字以及做者的姓名
 2 
 3 
 4     # 正向查詢
 5     queryResult=Book.objects
 6             .filter(publish__name="人民出版社")
 7             .values_list("title","authors__name")
 8     # 反向查詢
 9     queryResult=Publish.objects
10               .filter(name="人民出版社")
11               .values_list("book__title","book__authors__age","book__authors__name")
12 
13 
14 # 練習: 手機號以151開頭的做者出版過的全部書籍名稱以及出版社名稱
15 
16 
17     # 方式1:
18     queryResult=Book.objects
19             .filter(authors__authorDetail__telephone__regex="151")
20             .values_list("title","publish__name")
21     # 方式2:    
22     ret=Author.objects
23               .filter(authordetail__telephone__startswith="151")
24               .values("book__title","book__publish__name")

related_name

  反向查詢時,若是定義了related_name ,則用related_name替換 表名,例如:

1 publish  =  ForeignKey(Blog, related_name = 'bookList' )
1 # 練習: 查詢人民出版社出版過的全部書籍的名字與價格(一對多)
2 
3 # 反向查詢 再也不按表名:book,而是related_name:bookList
4 
5 
6     queryResult=Publish.objects
7               .filter(name="人民出版社")
8               .values_list("bookList__title","bookList__price")

 

 

五 聚合查詢、分組查詢、F查詢和Q查詢

 

聚合

  aggregate(*args, **kwargs)

1
2
3
4
# 計算全部圖書的平均價格
     >>>  from  django.db.models  import  Avg
     >>> Book.objects. all ().aggregate(Avg( 'price' )) #或者給它起名字:aggretate(a=Avg('price'))
     { 'price__avg' 34.35 }

  aggregate()QuerySet 的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。鍵的名稱是聚合值的標識符,值是計算出來的聚合值。鍵的名稱是按照字段和聚合函數的名稱自動生成出來的。若是你想要爲聚合值指定一個名稱,能夠向聚合子句提供它。

1
2
>>> Book.objects.aggregate(average_price = Avg( 'price' ))
{ 'average_price' 34.35 }

  若是你但願生成不止一個聚合,你能夠向aggregate()子句中添加另外一個參數。因此,若是你也想知道全部圖書價格的最大值和最小值,能夠這樣查詢:

1
2
3
>>>  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' )}
 

分組

 1 ###################################--單表分組查詢--#######################################################
 2 
 3 查詢每個部門名稱以及對應的員工數
 4 
 5 emp:
 6 
 7 id  name age   salary    dep
 8 1   alex  12   2000     銷售部
 9 2   egon  22   3000     人事部
10 3   wen   22   5000     人事部
11 
12 
13 sql語句:
14 select dep,Count(*) from emp group by dep;
15 
16 ORM:
17 emp.objects.values("dep").annotate(c=Count("id") #注意:annotate裏面必須寫個聚合函數,否則沒有意義,而且必須有個別名=,別名隨便寫,可是必須有,用哪一個字段分組,values裏面就寫哪一個字段,annotate其實就是對分組結果的統計,統計你須要什麼。
18 '''
19   select dep,count('id') as c from emp grouby dep;  #原生sql語句中的as c,不是必須有的
20 '''
21 
22 ###################################--多表分組查詢--###########################
23 
24 
25 多表分組查詢:
26 
27 查詢每個部門名稱以及對應的員工數
28 
29 
30 emp:
31 
32 id  name age   salary   dep_id
33 1   alex  12   2000       1
34 2   egon  22   3000       2
35 3   wen   22   5000       2
36 
37 
38 dep
39 
40 id   name 
41 1    銷售部
42 2    人事部
43 
44 
45 
46 emp-dep:
47 
48 id  name age   salary   dep_id   id   name 
49 1   alex  12   2000       1      1    銷售部
50 2   egon  22   3000       2      2    人事部
51 3   wen   22   5000       2      2    人事部
52 
53 
54 sql語句:
55 select dep.name,Count(*) from emp left join dep on emp.dep_id=dep.id group by dep.id
56 
57 ORM:
58 dep.objetcs.values("id").annotate(c=Count("emp")).values("name","c")
59 ret = models.Emp.objects.values('dep_id','name').annotate(a=Count(1)) 
60 '''
61   SELECT `app01_emp`.`dep_id`, `app01_emp`.`name`, COUNT(1) AS `a` FROM `app01_emp` GROUP BY `app01_emp`.`dep_id`, `app01_emp`.`name`
62 '''
63 #<QuerySet [{'dep_id': 1, 'name': 'alex', 'a': 1}, {'dep_id': 2, 'name': 'egon', 'a': 1}, {'dep_id': 2, 'name': 'wen', 'a': 1}]>,注意,這裏若是你寫了其餘字段,那麼只有這兩個字段重複,纔算一組,合併到一塊兒來統計個數
 
 
1 class Emp(models.Model):
2     name=models.CharField(max_length=32)
3     age=models.IntegerField()
4     salary=models.DecimalField(max_digits=8,decimal_places=2)
5     dep=models.CharField(max_length=32)
6     province=models.CharField(max_length=32) 

  annotate()爲調用的QuerySet中每個對象都生成一個獨立的統計值(統計方法用聚合函數)。

  總結 :跨表分組查詢本質就是將關聯表join成一張表,再按單表的思路進行分組查詢,,既然是join連表,就可使用我們的雙下劃線進行連表了。 

 1 #單表:
 2     #查詢每個部門的id以及對應員工的平均薪水
 3     ret = models.Emp.objects.values('dep_id').annotate(s=Avg('salary'))
 4     #查詢每一個部門的id以及對對應的員工的最大年齡
 5     ret = models.Emp.objects.values('dep_id').annotate(a=Max('age'))
 6     #Emp表示表,values中的字段表示按照哪一個字段group by,annotate裏面是顯示分組統計的是什麼
 7 
 8 #連表:
 9     # 查詢每一個部門的名稱以及對應的員工個數和員工最大年齡
10     ret = models.Emp.objects.values('dep__name').annotate(a=Count('id'),b=Max('age')) #注意,正向與反向的結果可能不一樣,若是反向查的時候,有的部門尚未員工,那麼他的數據也會被統計出來,只不過值爲0,可是正向查的話只能統計出來有員工的部門的相關數據,由於經過你是員工找部門,而不是經過部門找員工,結果集裏面的數據個數不一樣,可是你想要的統計結果是同樣的
11     #<QuerySet [{'a': 1, 'dep__name': '銷售部', 'b': 12}, {'a': 3, 'dep__name': '人事部', 'b': 22}]>
12     #使用雙下劃線進行連表,而後按照部門名稱進行分組,而後統計員工個數和最大年齡,最後結果裏面顯示的是部門名稱、個數、最大年齡。
13 #注意:若是values裏面有多個字段的狀況:
14 ret = models.Emp.objects.values('dep__name','age').annotate(a=Count('id'),b=Max('age')) #是按照values裏面的兩個字段進行分組,兩個字段同時相同纔算是一組,看下面的sql語句
15 ''' 
16     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`;
17 '''
18  

   下面是書籍表和出版社表的一個連表分組的sql語句寫法:

  

 

查詢練習

  (1) 練習:統計每個出版社的最便宜的書

1 publishList=Publish.objects.annotate(MinPrice=Min("book__price")) #若是沒有使用objects後面values或者values_list,獲得的結果是queryset類型,裏面是Publish的model對象,而且是對全部記
2 錄進行的統計,統計的Minprice也成了這些model對象裏面的一個屬性,這種連表分組統計的寫法最經常使用,思路也比較清晰
3 for publish_obj in publishList:
4     print(publish_obj.name,publish_obj.MinPrice)

  annotate的返回值是querySet,若是不想遍歷對象,能夠用上valuelist:

1 queryResult= Publish.objects
2             .annotate(MinPrice=Min("book__price"))
3             .values_list("name","MinPrice")
4 print(queryResult)
1 '''
2 SELECT "app01_publish"."name", MIN("app01_book"."price")  AS "MinPrice" FROM "app01_publish" 
3 LEFT  JOIN "app01_book" ON ("app01_publish"."nid" = "app01_book"."publish_id") 
4 GROUP BY "app01_publish"."nid", "app01_publish"."name", "app01_publish"."city", "app01_publish"."email" 
5 
6 '''

  (2) 練習:統計每一本書的做者個數

1 ret=Book.objects.annotate(authorsNum=Count('authors__name'))
2 ret=models.Book.objects.annotate(authorsNum=Count('authors__name')).values('title','authorsNum') #注意寫法,values裏面寫的個數的別名
3 ret=models.Book.objects.annotate(a=Count('author__name')).filter(a__gt=2).values('title','a') #還有這種寫法,看看你能不能明白這是在作什麼

  (3) 統計每一本以py開頭的書籍的做者個數:

1  queryResult=Book.objects
2            .filter(title__startswith="Py")
3            .annotate(num_authors=Count('authors')) #鏈接第三張表再鏈接author表,where title regexp '^Py' 而後按照連表後的大表中的book表的title字段進行分組,而且統計對應做者的個數

  (4) 統計不止一個做者的圖書:

1 queryResult=Book.objects
2           .annotate(num_authors=Count('authors'))
3           .filter(num_authors__gt=1) #filter也是也能夠是querset來調用

  (5) 根據一本圖書做者數量的多少對查詢集 QuerySet進行排序:

1 Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')

  (6) 查詢各個做者出的書的總價格:

1 #   按author表的全部字段 group by
2     queryResult=Author.objects
3               .annotate(SumPrice=Sum("book__price"))
4               .values_list("name","SumPrice")
5     print(queryResult)

F查詢與Q查詢

F查詢

  在上面全部的例子中,咱們構造的過濾器都只是將字段值與某個常量作比較。若是咱們要對兩個字段的值作比較,那該怎麼作呢?咱們在book表裏面加上兩個字段:評論數:commentNum,收藏數:KeepNum

  Django 提供 F() 來作這樣的比較。F() 的實例能夠在查詢中引用字段,來比較同一個 model 實例中兩個不一樣字段的值。

1 # 查詢評論數大於收藏數的書籍
2  
3    from django.db.models import F
4    Book.objects.filter(commentNum__lt=F('keepNum'))

  Django 支持 F() 對象之間以及 F() 對象和常數之間的加減乘除和取模的操做

1 # 查詢評論數大於收藏數2倍的書籍
2     Book.objects.filter(commentNum__lt=F('keepNum')*2)

  修改操做也可使用F函數,好比將每一本書的價格提升30元:

1 Book.objects.all().update(price=F("price")+30) 

Q查詢

  filter() 等方法中的關鍵字參數查詢都是一塊兒進行「AND」 的。 若是你須要執行更復雜的查詢(例如OR 語句),你可使用對象

1 from django.db.models import Q
2 Q(title__startswith='Py')

  Q 對象可使用&(與) 、|(或)、~(非) 操做符組合起來。當一個操做符在兩個Q 對象上使用時,它產生一個新的Q 對象。

bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))

  等同於下面的SQL WHERE 子句:

1 WHERE name ="yuan" OR name ="egon"

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

1 bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title")
2 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 對象,它必須位於全部關鍵字參數的前面。例如:

1 bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),
2                               title__icontains="python"  #也是and的關係,可是Q必須寫在前面
3                              )

綜合查詢練習題

 1 #1 查詢每一個做者的姓名以及出版的書的最高價格
 2     ret = models.Author.objects.values('name').annotate(max_price=Max('book__price'))
 3     print(ret) #注意:values寫在annotate前面是做爲分組依據用的,而且返回給你的值就是這個values裏面的字段(name)和分組統計的結果字段數據(max_price)
 4     # ret = models.Author.objects.annotate(max_price=Max('book__price')).values('name','max_price')#這種寫法是按照Author表的id字段進行分組,返回給你的是這個表的全部model對象,這個對象裏面包含着max_price這個屬性,後面寫values方法是獲取的這些對象的屬性的值,固然,能夠加雙下劃線來連表獲取其餘關聯表的數據,可是獲取的其餘關聯表數據是你的這些model對象對應的數據,而關聯獲取的數據可能不是你想要的最大值對應的那些數據
 5 # 2 查詢做者id大於2做者的姓名以及出版的書的最高價格
 6     ret = models.Author.objects.filter(id__gt=2).annotate(max_price=Max('book__price')).values('name','max_price')#記着,這個values取得是前面調用這個方法的表的全部字段值以及max_pirce的值,這也是爲何咱們取關聯數據的時候要加雙劃線的緣由
 7     print(ret)
 8 
 9 #3 查詢做者id大於2或者做者年齡大於等於20歲的女做者的姓名以及出版的書的最高價格
10     # 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')
11 #4 查詢每一個做者出版的書的最高價格 的平均值
12     # ret = models.Author.objects.values('id').annotate(max_price=Max('book__price')).aggregate(Avg('max_price')) #{'max_price__avg': 555.0} 注意,aggregate是queryset的終止句,獲得的是字典
13     # ret = models.Author.objects.annotate(max_price=Max('book__price')).aggregate(Avg('max_price')) #{'max_price__avg': 555.0} 注意,aggregate是queryset的終止句,獲得的是字典
14 
15 #5 每一個做者出版的全部書的最高價格以及最高價格的那本書的名稱(經過orm玩起來就是個死題,須要用原生sql)
16     '''
17     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=
18 app01_book_authors.author_id INNER JOIN app01_book on app01_book.id=
19 app01_book_authors.book_id ORDER BY app01_book.price desc) as b  GROUP BY id
20 '''
21 
22     print(ret)

 

 

 六 ORM執行原生sql語句

   在模型查詢API不夠用的狀況下,咱們還可使用原始的SQL語句進行查詢。

  Django 提供兩種方法使用原始SQL進行查詢:一種是使用raw()方法,進行原始SQL查詢並返回模型實例;另外一種是徹底避開模型層,直接執行自定義的SQL語句。

  執行原生查詢

    raw()管理器方法用於原始的SQL查詢,並返回模型的實例:

    注意:raw()語法查詢必須包含主鍵。

    這個方法執行原始的SQL查詢,並返回一個django.db.models.query.RawQuerySet 實例。 這個RawQuerySet 實例能夠像通常的QuerySet那樣,經過迭代來提供對象實例。

    舉個例子:

1 class Person(models.Model):
2     first_name = models.CharField(...)
3     last_name = models.CharField(...)
4     birth_date = models.DateField(...)

  能夠像下面這樣執行原生SQL語句

1 >>> for p in Person.objects.raw('SELECT * FROM myapp_person'):
2 ...     print(p)

  raw()查詢能夠查詢其餘表的數據。

    舉個例子:

1 ret = models.Student.objects.raw('select id, tname as hehe from app02_teacher')
2     for i in ret:
3         print(i.id, i.hehe)

  raw()方法自動將查詢字段映射到模型字段。還能夠經過translations參數指定一個把查詢的字段名和ORM對象實例的字段名互相對應的字典

1 d = {'tname': 'haha'}
2     ret = models.Student.objects.raw('select * from app02_teacher', translations=d)
3     for i in ret:
4         print(i.id, i.sname, i.haha)

  原生SQL還可使用參數,注意不要本身使用字符串格式化拼接SQL語句,防止SQL注入!

1 d = {'tname': 'haha'}
2     ret = models.Student.objects.raw('select * from app02_teacher where id > %s', translations=d, params=[1,])
3     for i in ret:
4         print(i.id, i.sname, i.haha)

  直接執行自定義SQL

    有時候raw()方法並不十分好用,不少狀況下咱們不須要將查詢結果映射成模型,或者咱們須要執行DELETE、 INSERT以及UPDATE操做。在這些狀況下,咱們能夠直接訪問數據庫,徹底避開模型層。

    咱們能夠直接從django提供的接口中獲取數據庫鏈接,而後像使用pymysql模塊同樣操做數據庫。

1 from django.db import connection, connections
2 cursor = connection.cursor()  # cursor = connections['default'].cursor()
3 cursor.execute("""SELECT * from auth_user where id = %s""", [1])
4 ret = cursor.fetchone()

 

七 Python腳本中調用Django環境(django外部腳本使用models)

 若是你想經過本身建立的python文件在django項目中使用django的models,那麼就須要調用django的環境:

 1 import os
 2 
 3 if __name__ == '__main__':
 4     os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
 5     import django
 6     django.setup()
 7 
 8     from app01 import models  #引入也要寫在上面三句以後
 9 
10     books = models.Book.objects.all()
11     print(books)

 

八 補充多個app配置models

  app01的models文件內容

1 from django.db import models
2 
3 # Create your models here.
4 
5 class UserInfo(models.Model):
6 
7     name = models.CharField(max_length=12)

  app02的models文件內容

1 from django.db import models
2 
3 # Create your models here.
4 
5 
6 class Class(models.Model):
7 
8     title = models.CharField(max_length=32)
9     user = models.ForeignKey('app01.Userinfo')  #若是須要兩個app之間的models進行關聯,直接這樣寫就能夠,或者直接將那個被關聯的表,經過import的方法引入進行進行關聯。

 

   不須要進行其餘的配置了,直接執行數據庫同步指令就能夠了。

   關於多個app多個數據庫,而且數據有關聯時的一些玩法,等後面我再補充吧

相關文章
相關標籤/搜索