Django--models--多表操做

一 建立模型

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

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

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

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

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

Book

   id    title   price  publish    
     php     100   人民出版社  
     python  200   老男孩出版社   
     go      100   人民出版社  
     java    300   人民出版社  


爲了存儲出版社的郵箱,地址,在第一個表後面加字段

Book

   id    title   price  publish    email    addr    
     php     100   人民出版社   111      北京
     python  200   老男孩出版社 222      上海
     go      100   人民出版社   111      北京
     java    300   人民出版社   111      北京
   
這樣會有大量重複的數據,浪費空間


####################################################################################


一對多:一個出版社對應多本書(關聯信息建在多的一方,也就是book表中)

Book

   id    title   price     publish_id   
     php     100         1
     python  200         1
     go      100         2  
     java    300         1


Publish

    id    name       email    addr    
   人民出版社   111      北京       
   沙河出版社   222      沙河



總結:一旦肯定表關係是一對多:在多對應的表中建立關聯字段(在多的表裏建立關聯字段)  ,publish_id


查詢python這本書的出版社的郵箱(子查詢)

   select publish_id from Book where title=「python」
   select email from Publish where id=1


####################################################################################


多對多:一本書有多個做者,一個做者出多本書

Book

   id    title   price     publish_id    
     php     100         1               
     python  200         1
     go      100         2  
     java    300         1



Author
         id  name  age   addr
   alex  34   beijing
   egon  55   nanjing



Book2Author

    id    book_id  author_id
      2         1
      2         2
      3         2

總結:一旦肯定表關係是多對多:建立第三張關係表(建立中間表,中間表就三個字段,本身的id,書籍id和做者id) :
          
          id      book_id   author_id


# alex出版過的書籍名稱(子查詢)

select id from Author where name='alex'

select book_id from Book2Author where  author_id=1

select title from Book where id =book_id

####################################################################################


一對一:對做者詳細信息的擴展(做者表和做者詳情表)

Author
         id  name  age     ad_id(UNIQUE) 
   alex  34       1     
   egon  55       2     


AuthorDetail

   id    addr      gender    tel   gf_name   author_id(UNIQUE)
  beijing    male      110   小花           1
  nanjing    male      911   槓娘           2


總結: 一旦肯定是一對一的關係:在兩張表中的任意一張表中創建關聯字段+Unique






====================================


Publish  
Book
Author
AuthorDetail
Book2Author



CREATE TABLE publish(
                id INT PRIMARY KEY auto_increment ,
                name VARCHAR (20)
              );


CREATE TABLE book(
                id INT PRIMARY KEY auto_increment ,
                title VARCHAR (20),
                price DECIMAL (8,2),
                pub_date DATE ,
                publish_id INT ,
                FOREIGN KEY (publish_id) REFERENCES publish(id)
              );


CREATE TABLE authordetail(
                id INT PRIMARY KEY auto_increment ,
                tel VARCHAR (20)
              );

CREATE TABLE author(
                id INT PRIMARY KEY auto_increment ,
                name VARCHAR (20),
                age INT,
                authordetail_id INT UNIQUE ,
                FOREIGN KEY (authordetail_id) REFERENCES authordetail(id)
              );



CREATE  TABLE book2author(
       id INT PRIMARY KEY auto_increment ,
       book_id INT ,
       author_id INT ,
       FOREIGN KEY (book_id) REFERENCES book(id),
       FOREIGN KEY (author_id) REFERENCES author(id)
)

分析以下
分析以下

注意:關聯字段與外鍵約束沒有必然的聯繫(建管理字段是爲了進行查詢,建約束是爲了避免出現髒數據)數據庫

在Models建立以下模型django

class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish_date = models.DateField()
    # 閱讀數
    # reat_num=models.IntegerField(default=0)
    # 評論數
    # commit_num=models.IntegerField(default=0)

    publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE)
    authors=models.ManyToManyField(to='Author')
    def __str__(self):
        return self.name


class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to='AuthorDatail',to_field='nid',unique=True,on_delete=models.CASCADE)


class AuthorDatail(models.Model):
    nid = models.AutoField(primary_key=True)
    telephone = models.BigIntegerField()
    birthday = models.DateField()
    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()
View Code

注意事項:app

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

 

二 添加表記錄

一對多的

方式1:
   publish_obj=Publish.objects.get(nid=1)
   book_obj=Book.objects.create(title="",publishDate="2012-12-12",price=100,publish=publish_obj)
  
方式2:
   book_obj=Book.objects.create(title="",publishDate="2012-12-12",price=100,publish_id=1)

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

 關鍵點:

    一 book_obj.publish=Publish.objects.filter(id=book_obj.publish_id).first()

    二 book_obj.authors.all()
       關鍵點:book.authors.all()  # 與這本書關聯的做者集合

        1 book.id=3
        2 book_authors
            id  book_id  author_ID
            3      3             1
            4      3             2

        3  author
           id   name
           1   alex
           2   egon

    book_obj.authors.all()    ------->   [alex,egon]
    # -----一對多添加
    pub=Publish.objects.create(name='egon出版社',email='445676@qq.com',city='山東')
    print(pub)

    # 爲book表綁定和publish的關係
    import datetime,time
    now=datetime.datetime.now().__str__()
    now = datetime.datetime.now().strftime('%Y-%m-%d')
    print(type(now))
    print(now)
    # 日期類型必須是日期對象或者字符串形式的2018-09-12(2018-9-12),其它形式不行
    Book.objects.create(name='海燕3',price=333.123,publish_date=now,publish_id=2)
    Book.objects.create(name='海3燕3',price=35.123,publish_date='2018/02/28',publish=pub)
    pub=Publish.objects.filter(nid=1).first()
    book=Book.objects.create(name='測試書籍',price=33,publish_date='2018-7-28',publish=pub)
    print(book.publish.name)
    # 查詢出版了紅樓夢這本書出版社的郵箱
    book=Book.objects.filter(name='紅樓夢').first()
    print(book.publish.email)

多對多

# 當前生成的書籍對象
    book_obj=Book.objects.create(title="追風箏的人",price=200,publishDate="2012-11-12",publish_id=1)
    # 爲書籍綁定的作做者對象
    yuan=Author.objects.filter(name="yuan").first() # 在Author表中主鍵爲2的紀錄
    egon=Author.objects.filter(name="alex").first() # 在Author表中主鍵爲1的紀錄

    # 綁定多對多關係,即向關係表book_authors中添加紀錄
    book_obj.authors.add(yuan,egon)    #  將某些特定的 model 對象添加到被關聯對象集合中。   =======    book_obj.authors.add(*[])
    book = Book.objects.filter(name='紅樓夢').first()
    egon=Author.objects.filter(name='egon').first()
    lqz=Author.objects.filter(name='lqz').first()
    # 1 沒有返回值,直接傳對象
    book.authors.add(lqz,egon)
    # 2 直接傳做者id
    book.authors.add(1,3)
    # 3 直接傳列表,會打散
    book.authors.add(*[1,2])
    # 解除多對多關係
    book = Book.objects.filter(name='紅樓夢').first()
    # 1 傳做者id
    book.authors.remove(1)
    # 2 傳做者對象
    egon = Author.objects.filter(name='egon').first()
    book.authors.remove(egon)
    #3 傳*列表
    book.authors.remove(*[1,2])
    #4 刪除全部
    book.authors.clear()
    # 5 拿到與 這本書關聯的全部做者,結果是queryset對象,做者列表
    ret=book.authors.all()
    # print(ret)
    # 6 queryset對象,又能夠繼續點(查詢紅樓夢這本書全部做者的名字)
    ret=book.authors.all().values('name')
    print(ret)
    # 以上總結:
    # (1)
    # book=Book.objects.filter(name='紅樓夢').first()
    # print(book)
    # 在點publish的時候,其實就是拿着publish_id又去app01_publish這個表裏查數據了
    # print(book.publish)
    # (2)book.authors.all()

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

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

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

 

三 基於對象的跨表查詢

一對多查詢(publish與book)

正向查詢(按字段:publish)

# 查詢主鍵爲1的書籍的出版社所在的城市
book_obj=Book.objects.filter(pk=1).first()
# book_obj.publish 是主鍵爲1的書籍對象關聯的出版社對象
print(book_obj.publish.city)

反向查詢(按表名:book_set)

publish=Publish.objects.get(name="蘋果出版社")
#publish.book_set.all() : 與蘋果出版社關聯的全部書籍對象集合
book_list=publish.book_set.all()    
for book_obj in book_list:
       print(book_obj.title)
   # 一對多正向查詢
    book=Book.objects.filter(name='紅樓夢').first()
    print(book.publish)#與這本書關聯的出版社對象
    print(book.publish.name)
    # 一對多反向查詢
    # 人民出版社出版過的書籍名稱
    pub=Publish.objects.filter(name='人民出版社').first()
    ret=pub.book_set.all()
    print(ret)

一對一查詢(Author 與 AuthorDetail)

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

egon=Author.objects.filter(name="egon").first()
print(egon.authorDetail.telephone)


反向查詢(按表名:author):
# 查詢全部住址在北京的做者的姓名
 
authorDetail_list=AuthorDetail.objects.filter(addr="beijing")
for obj in authorDetail_list:
     print(obj.author.name)


    # 一對一正向查詢
    # lqz的手機號
    lqz=Author.objects.filter(name='lqz').first()
    tel=lqz.author_detail.telephone
    print(tel)
    # 一對一反向查詢
    # 地址在北京的做者姓名
    author_detail=AuthorDatail.objects.filter(addr='北京').first()
    name=author_detail.author.name
    print(name)

多對多查詢 (Author 與 Book)

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

# 眉全部做者的名字以及手機號
 
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):

# 查詢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)
    # 正向查詢----查詢紅樓夢全部做者名稱
    book=Book.objects.filter(name='紅樓夢').first()
    ret=book.authors.all()
    print(ret)
    for auth in ret:
        print(auth.name)
    # 反向查詢 查詢lqz這個做者寫的全部書
    author=Author.objects.filter(name='lqz').first()
    ret=author.book_set.all()
    print(ret)
View Code

注意:

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

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

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

  
  # 查詢 人民出版社出版過的全部書籍
 
  publish = Publish.objects.get(name = "人民出版社" )
  book_list = publish.bookList. all ()   # 與人民出版社關聯的全部書籍對象集合

 

四 基於雙下劃線的跨表查詢

Django 還提供了一種直觀而高效的方式在查詢(lookups)中表示關聯關係,它能自動確認 SQL JOIN 聯繫。要作跨關係查詢,就使用兩個下劃線來連接模型(model)間關聯字段的名稱,直到最終連接到你想要的model 爲止。

正向查詢按字段,反向查詢按表名小寫用來告訴ORM引擎join哪張表

 

一對多查詢

# 練習:  查詢蘋果出版社出版過的全部書籍的名字與價格(一對多)

    # 正向查詢 按字段:publish

    queryResult=Book.objects
            .filter(publish__name="蘋果出版社")
            .values_list("title","price")

    # 反向查詢 按表名:book

    queryResult=Publish.objects
              .filter(name="蘋果出版社")
              .values_list("book__title","book__price")
查詢的本質同樣,就是select from的表不同
    # 正向查詢按字段,反向查詢按表名小寫
    # 查詢紅樓夢這本書出版社的名字
    # select * from app01_book inner join app01_publish
    # on app01_book.publish_id=app01_publish.nid
    ret=Book.objects.filter(name='紅樓夢').values('publish__name')
    print(ret)
    ret=Publish.objects.filter(book__name='紅樓夢').values('name')
    print(ret)

多對多查詢 

# 練習: 查詢alex出過的全部書籍的名字(多對多)

    # 正向查詢 按字段:authors:
    queryResult=Book.objects
            .filter(authors__name="yuan")
            .values_list("title")

    # 反向查詢 按表名:book
    queryResult=Author.objects
              .filter(name="yuan")
              .values_list("book__title","book__price")
    # 正向查詢按字段,反向查詢按表名小寫
    # 查詢紅樓夢這本書出版社的名字
    # select * from app01_book inner join app01_publish
    # on app01_book.publish_id=app01_publish.nid
    ret=Book.objects.filter(name='紅樓夢').values('publish__name')
    print(ret)
    ret=Publish.objects.filter(book__name='紅樓夢').values('name')
    print(ret)
    # sql 語句就是from的表不同
    # -------多對多正向查詢
    # 查詢紅樓夢全部的做者
    ret=Book.objects.filter(name='紅樓夢').values('authors__name')
    print(ret)
    # ---多對多反向查詢
    ret=Author.objects.filter(book__name='紅樓夢').values('name')
    ret=Author.objects.filter(book__name='紅樓夢').values('name','author_detail__addr')
    print(ret)

 

 

一對一查詢

# 查詢alex的手機號
    
    # 正向查詢
    ret=Author.objects.filter(name="alex").values("authordetail__telephone")

    # 反向查詢
    ret=AuthorDetail.objects.filter(author__name="alex").values("telephone")

 

    # 查詢lqz的手機號
    # 正向查
    ret=Author.objects.filter(name='lqz').values('author_detail__telephone')
    print(ret)
    # 反向查
    ret= AuthorDatail.objects.filter(author__name='lqz').values('telephone')
    print(ret)

進階練習(連續跨表)

 

# 練習: 查詢人民出版社出版過的全部書籍的名字以及做者的姓名


    # 正向查詢
    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")
  # ----進階練習,連續跨表
    # 查詢手機號以33開頭的做者出版過的書籍名稱以及書籍出版社名稱
    # author_datail author book publish
    # 基於authorDatail表
    ret=AuthorDatail.objects.filter(telephone__startswith='33').values('author__book__name','author__book__publish__name')
    print(ret)
    # 基於Author表
    ret=Author.objects.filter(author_detail__telephone__startswith=33).values('book__name','book__publish__name')
    print(ret)
    # 基於Book表
    ret=Book.objects.filter(authors__author_detail__telephone__startswith='33').values('name','publish__name')
    print(ret)
    # 基於Publish表
    ret=Publish.objects.filter(book__authors__author_detail__telephone__startswith='33').values('book__name','name')
    print(ret)

related_name

publish = ForeignKey(Blog, related_name='bookList')
反向查詢時,若是定義了related_name ,則用related_name替換表名,例如:
# 練習: 查詢人民出版社出版過的全部書籍的名字與價格(一對多)

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


    queryResult=Publish.objects
              .filter(name="人民出版社")
              .values_list("bookList__title","bookList__price") 

 

五 聚合查詢與分組查詢

聚合

aggregate(*args, **kwargs)

# 計算全部圖書的平均價格

     >>>  from  django.db.models  import  Avg
     >>> Book.objects. all ().aggregate(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, MaxMin
>>> Book.objects.aggregate(Avg( 'price' ),  Max ( 'price' ),  Min ( 'price' ))
{ 'price__avg' 34.35 'price__max' : Decimal( '81.20' ),  'price__min' : Decimal( '12.99' )}
 
    # 查詢全部書籍的平均價格
    from django.db.models import Avg,Count,Max,Min
    ret=Book.objects.all().aggregate(Avg('price'))
    # {'price__avg': 202.896}
    # 能夠更名字
    ret=Book.objects.all().aggregate(avg_price=Avg('price'))
    # 統計平均價格和最大價格
    ret=Book.objects.all().aggregate(avg_price=Avg('price'),max_price=Max('price'))
    # 統計最小价格
    ret = Book.objects.all().aggregate(avg_price=Avg('price'), min_price=Min('price'))
    # 統計個數和平均價格
    ret = Book.objects.all().aggregate(avg_price=Avg('price'), max_price=Max('price'),count=Count('price'))
    ret = Book.objects.all().aggregate(avg_price=Avg('price'), max_price=Max('price'),count=Count('nid'))
    print(ret)

分組

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

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

emp:

id  name age   salary    dep
  alex  12   2000     銷售部
  egon  22   3000     人事部
  wen   22   5000     人事部


sql語句:
select dep,Count(*) from emp group by dep;

ORM:
emp.objects.values("dep").annotate(c=Count("id")


# 示例:查詢每個部門的名稱,以及平均薪水
# select dep,Avg(salary) from app01_emp group by dep
from django.db.models import Avg, Count, Max, Min
# ret=Emp.objects.values('dep').annotate(Avg('salary'))
# 從新命名
ret=Emp.objects.values('dep').annotate(avg_salary=Avg('salary'))
print(ret)
總結:單表分組查詢orm語法:單表模型.objects.values('group by 的字段').annotate(聚合函數('統計的字段'))
  查詢每個省份名稱以及對應的員工數
  Emp.objects.values('province').annotate(Count('id'))
 
###################################--多表分組查詢--###########################
     Book表

        id   title    date      price  publish_id
   紅樓夢    2012-12-12    101       1
   西遊記    2012-12-12    101       1
   三國演繹    2012-12-12    101       1
   金梅    2012-12-12    301       2


     Publish表
        id    name      addr   email
   人民出版社    北京       123@qq.com
   南京出版社    南京       345@163.com

多表分組查詢:
查詢每一個出版社出版的書籍個數
  ret=Book.objects.values('publish_id').annotate(Count('nid'))
  print(ret)
查詢每一個出版社的名稱和書籍個數(先join,再分組)
    SELECT app01_publish.name,COUNT(app01_book.name) from app01_book
        INNER JOIN app01_publish
        on app01_publish.nid = app01_book.publish_id
        GROUP BY app01_publish.nid
  orm實現:
  ret=Publish.objects.values('name').annotate(Count('book__name'))
    ret=Publish.objects.values('nid').annotate(c=Count('book__name')).values('name','c')
    print(ret)
模型總結:跨表查詢的模型:每個後表模型.objects.value('pk').annotate(聚合函數('關聯表__統計字段')).values()
    # 查詢每一個做者的名字,以及出版過書籍的最高價格
    ret=Author.objects.values('pk').annotate(c=Max('book__price')).values('name','c')
    print(ret)
    # 查詢每個書籍的名稱,以及對應的做者個數
    ret=Book.objects.values('pk').annotate(c=Count('authors__name')).values('name','c')
    print(ret)

 

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成一張表,再按單表的思路進行分組查詢。 

查詢練習

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

publishList=Publish.objects.annotate(MinPrice=Min("book__price"))

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

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

 queryResult=Book.objects
           .filter(title__startswith="Py")
           .annotate(num_authors=Count('authors'))

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

queryResult=Book.objects
          .annotate(num_authors=Count('authors'))
          .filter(num_authors__gt=1)

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

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

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

#   按author表的全部字段 group by
    queryResult=Author.objects
              .annotate(SumPrice=Sum("book__price"))
              .values_list("name","SumPrice")
    print(queryResult)
 # ————————————單表下的分組查詢
    '''
    查詢每個部門名稱以及對應的員工數
    emp:
    id  name age   salary    dep
    1   alex  12   2000     銷售部
    2   egon  22   3000     人事部
    3   wen   22   5000     人事部
    '''
    # select count(id) from emp group by dep
    # 示例一:查詢每個部門的名稱,以及平均薪水
    # select dep,Avg(salary) from app01_emp group by dep
    from django.db.models import Avg, Count, Max, Min
    ret=Emp.objects.values('dep').annotate(Avg('salary'))
    # 從新命名
    ret=Emp.objects.values('dep').annotate(avg_salary=Avg('salary'))
    print(ret)
    # ---*******單表分組查詢ORM總結:表名.objects.values('group by 的字段').annotate(聚合函數('統計的字段'))
    # 示例2 查詢每一個省份對應的員工數
    ret=Emp.objects.values('province').annotate(Count('id'))
    ret=Emp.objects.values('province').annotate(c=Count('id'))
    print(ret)
    # 補充知識點:
    ret=Emp.objects.all()
    # select * from emp
    ret=Emp.objects.values('name')
    # select name from emp
    # ****單表下,按照id進行分組是沒有任何意義的
    ret=Emp.objects.all().annotate(Avg('salary'))
    print(ret)
    # ******多表分組查詢
    # 查詢每個出版社出版的書籍個數
    ret=Book.objects.values('publish_id').annotate(Count('nid'))
    print(ret)
    # 查詢每一個出版社的名稱以及出版社書的個數(先join在跨表分組)
    # 正向
    ret=Publish.objects.values('name').annotate(Count('book__name'))
    ret=Publish.objects.values('nid').annotate(c=Count('book__name')).values('name','c')
    print(ret)
    # 反向
    ret=Book.objects.values('publish__name').annotate(Count('name'))
    ret=Book.objects.values('publish__name').annotate(c=Count('name')).values('publish__name','c')
    print(ret)
    # 查詢每一個做者的名字,以及出版過書籍的最高價格
    ret=Author.objects.values('pk').annotate(c=Max('book__price')).values('name','c')
    print(ret)
    # 跨表查詢的模型:每個後表模型.objects.value('pk').annotate(聚合函數('關聯表__統計字段')).values()

    # 查詢每個書籍的名稱,以及對應的做者個數
    ret=Book.objects.values('pk').annotate(c=Count('authors__name')).values('name','c')
    print(ret)
    # 統計不止一個做者的圖書
    ret=Book.objects.values('pk').annotate(c=Count('authors__name')).filter(c__gt=1).values('name','c')
    print(ret)
View Code

六 F查詢與Q查詢

F查詢

在上面全部的例子中,咱們構造的過濾器都只是將字段值與某個常量作比較。若是咱們要對兩個字段的值作比較,那該怎麼作呢?

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

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

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

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

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

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

 

Q查詢

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

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

查詢函數能夠混合使用Q 對象和關鍵字參數。全部提供給查詢函數的參數(關鍵字參數或Q 對象)都將"AND」在一塊兒。可是,若是出現Q 對象,它必須位於全部關鍵字參數的前面。例如:

bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),
                              title__icontains="python"
                             )

 

 

 # 查詢評論數大於閱讀數的書籍
    from django.db.models import F,Q
    # select * from book where commit_num>read_num;
    # 這樣確定是不行的
    # Book.objects.filter(commit_num__gt=read_num)
    ret=Book.objects.filter(commit_num__gt=F('reat_num'))
    print(ret)
    # 把全部書籍的價格加10
    Book.objects.all().update(price=F('price')+10)
    # ----Q函數,描述一個與,或,非的關係
    # 查詢名字叫紅樓夢或者價格大於100的書
    ret=Book.objects.filter(Q(name='紅樓夢')|Q(price__gt=100))
    print(ret)
    # 查詢名字叫紅樓夢和價格大於100的書
    ret = Book.objects.filter(Q(name='紅樓夢') & Q(price__gt=100))
    print(ret)
    # # 等同於
    ret2=Book.objects.filter(name='紅樓夢',price__gt=100)
    print(ret2)
    # 也能夠Q套Q
    # 查詢名字叫紅樓夢和價格大於100  或者 nid大於2
    ret=Book.objects.filter((Q(name='紅樓夢') & Q(price__gt=100))|Q(nid__gt=2))
    print(ret)
    # ----非
    ret=Book.objects.filter(~Q(name='紅樓夢'))
    print(ret)
    # Q和鍵值對聯合使用,可是鍵值對必須放在Q的後面(描述的是一個且的關係)
    # 查詢名字不是紅樓夢,而且價格大於100的書
    ret=Book.objects.filter(~Q(name='紅樓夢'),price__gt=100)
    print(ret)

 

# 查詢每個出版社出版的書籍個數ret=Book.objects.values('publish_id').annotate(Count('nid'))print(ret)
相關文章
相關標籤/搜索