Django基礎之:多表操作


一 創建模型

  表和表之間的關系

    一對一、多對一、多對多 ,用book表和publish表自己來想想關系,想想里面的操作,加外鍵約束和不加外鍵約束的區別,一對一的外鍵約束是在一對多的約束上加上唯一約束。

  實例:我們來假定下面這些概念,字段和關系

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

  作者詳細模型:把作者的詳情放到詳情表,包含生日,手機號,家庭住址等信息。作者詳情模型和作者模型之間是一對一的關系(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

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這個參數的時候,最后后面的值是個字符串,不然你就需要將你要關聯的那個表放到這個表的上面

   

  關於多對多表的三種創建方式(目前你先作為了解)

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

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")

  

    注意:

      當我們需要在第三張關系表中存儲額外的字段時,就要使用第三種方式,第三種方式還是可以使用多對多關聯關系操作的接口(all、add、clear等等)

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

 

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()方法排序過的數據)

創建表時的一些元信息設置
元信息

獲取元信息,可以通過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:

class Info(models.Model):  
    ''''' 
            信息統計 
    '''  
    app_id = models.ForeignKey(App)  
    app_name = models.CharField(verbose_name='應用名',  max_length=32, db_column='app_name2')  
  
    class Meta:  
        db_table = 'info'  
        verbose_name = '信息統計'  
        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 。

   咱們的表里面包含了一對一、一對多、多對多的關系,我們基於這幾個表來練習,將來無論有多少張表,都逃脫不了這三個關系,操作起來都是一樣的。

 

    關於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(可執行對象)

關於on_delete參數
關於on_delete
ForeignKey的db_contraint參數

關系和約束大家要搞清楚,我不加外鍵能不能表示兩個表之間的關系啊,當然可以

但是我們就不能使用ORM外鍵相關的方法了,所以我們單純的將外鍵換成一個其他字段類型,只是單純的存着另外一個關聯表的主鍵值是不能使用ORM外鍵方法的。

#db_constraint=False只加兩者的關系,沒有強制約束的效果,並且ORM外鍵相關的接口(方法)還能使用,所以如果將來公司讓你建立外鍵,並且不能有強制的約束關系,那么就可以將這個參數改為False
    customer = models.ForeignKey(verbose_name='關聯客戶', to='Customer',db_constraint=False)
ForeignKey的db_contraint參數

二 添加表記錄

  

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

  publish表:

    

  author表:

    

  authordetail表:

    

  

  一對多

方式1:
   publish_obj=Publish.objects.get(nid=1) #拿到nid為1的出版社對象
   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類的對象肯定會報錯的,別亂昂
  
方式2:
   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是什么? 

   

  多對多

   方式一: 多對多一般在前端頁面上使用的時候是多選下拉框的樣子來給用戶選擇多個數據,這里可以讓用戶選擇多個書籍,多個作者
  # 當前生成的書籍對象 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值

   

  

  數據庫表紀錄生成如下:

  book表 

    

  book_authors表

    

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

 

  多對多關系其它常用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',]) #這么寫也可以,但是注意列表中的元素是字符串,列表前面沒有*,之前我測試有*,感覺是版本的問題,沒事,能夠用哪個用哪個

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

更新:
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) #將數據和作者的多對多關系加上

刪除:
models.Book.objects.filter(id=1).delete()

 

 

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

 

 

三 基於對象的跨表查詢

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

一對多查詢(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)

   

一對一查詢(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

 

 多對多查詢(Author與Book)

  

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

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

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

1
2
3
4
5
6
# 查詢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 中做一下更改:

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

    那么接下來就會如我們看到這般:

1
2
3
4
# 查詢 人民出版社出版過的所有書籍
 
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")             .values_list("title","publish__name") # 方式2: ret=Author.objects .filter(authordetail__telephone__startswith="151") .values("book__title","book__publish__name")

related_name

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

1
publish  =  ForeignKey(Blog, related_name = 'bookList' )
# 練習: 查詢人民出版社出版過的所有書籍的名字與價格(一對多)

# 反向查詢 不再按表名:book,而是related_name:bookList

queryResult=Publish.objects               .filter(name="人民出版社")               .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' )}
 

分組

 
###################################--單表分組查詢--#######################################################

查詢每一個部門名稱以及對應的員工數

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)) 
'''
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}]>,注意,這里如果你寫了其他字段,那么只有這兩個字段重復,才算一組,合並到一起來統計個數

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)

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

  總結 :跨表分組查詢本質就是將關聯表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`;
'''

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

  

F查詢與Q查詢

F查詢

  在上面所有的例子中,我們構造的過濾器都只是將字段值與某個常量做比較。如果我們要對兩個字段的值做比較,那該怎么做呢?我們在book表里面加上兩個字段:評論數:commentNum,收藏數:KeepNum

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

1
2
3
4
# 查詢評論數大於收藏數的書籍
 
    from  django.db.models  import  F
    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
2
from  django.db.models  import  Q
Q(title__startswith = 'Py' )

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

1
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" )
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
2
3
bookList = Book.objects. filter (Q(publishDate__year = 2016 ) | Q(publishDate__year = 2017 ),
                               title__icontains = "python"  #也是and的關系,但是Q必須寫在前面
                              )

 

六 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()

七 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)

 

八 補充多個app配置models

  app01的models文件內容

from django.db import models

# Create your models here.

class UserInfo(models.Model):

    name = models.CharField(max_length=12)

 

  app02的models文件內容

from django.db import models

# Create your models here.


class Class(models.Model):

    title = models.CharField(max_length=32)
    user = models.ForeignKey('app01.Userinfo')  #如果需要兩個app之間的models進行關聯,直接這樣寫就可以,或者直接將那個被關聯的表,通過import的方法引入進行進行關聯。

 

   不需要進行其他的配置了,直接執行數據庫同步指令就可以了。

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM