WEB框架-Django框架學習(二)- 模型層

今日份整理爲模型層python

1.ORM簡介

MVC或者MVC框架中包括一個重要的部分,就是ORM,它實現了數據模型與數據庫的解耦,即數據模型的設計不須要依賴於特定的數據庫,經過簡單的配置就能夠輕鬆更換數據庫,這極大的減輕了開發人員的工做量,不須要面對因數據庫變動而致使的無效勞動ORM是「對象-關係-映射」的簡稱。mysql

不過使用ORM也是有優缺點的git

優勢:正則表達式

  • 寫python代碼,實現對數據庫的相關操做,提升開發效率
  • 平滑切換數據庫,在多表連接的時候是很明顯的

缺點:sql

  • python代碼,轉換SQL須要時間,效率相對會有影響
  • SQL能力降低;

若是想要打印orm轉換過程當中的sql,須要在setting中進行以下配置數據庫

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}  

settings配置

2. ORM的單表操做

2.1 ORM的單表建立

2.1.1mysql數據庫建立庫

#在mysql客戶端中新建庫
create database 數據庫名稱 default charset=utf8;

2.1.2.在settings配置數據庫

#在項目中的公共settings中設置
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 注意改成mysql 'NAME': "數據庫名稱", 'HOST': "127.0.0.1", 'PORT': 3306, 'USER': '用戶名', 'PASSWORD': "密碼", } }

2.1.3.跟settings同級的init.py 配置

#在__init__中配置
 import pymysql
 pymysql.install_as_MySQLdb()

2.1.4.在app下models.py 裏寫模型類

#在app中的models.py中建立類
class Book(models.Model):
           nid = models.AutoField(primary_key=True)
           title = models.CharField(max_length=32)
           price = models.DecimalField(max_digits=5, decimal_places=2)
           publish = models.CharField(max_length=64)
           pub_date = models.DateField()

注意:在這裏定義類的屬性要和數據庫中的屬性一致,吃了太多由於一個字母錯了,找了大半天的錯。。。django

2.1.5.在命令行,執行命令

#在命令行中輸入命令
python manage.py makemigrations   # 把models變動記錄記錄下來。注意:此時,數據還沒數據。
python manage.py migrate  # 將數據導入到數據。同步。

補充信息後端

每一個字段有一些特有的參數,例如,CharField須要max_length參數來指定VARCHAR數據庫字段的大小。還有一些適用於全部字段的通用參數。 這些參數在文檔中有詳細定義,這裏咱們只簡單介紹一些最經常使用的:服務器

更多字段:app

<1> CharField
       字符串字段, 用於較短的字符串.
       CharField 要求必須有一個參數 maxlength, 用於從數據庫層和Django校驗層限制該字段所容許的最大字符數.

<2> IntegerField
      #用於保存一個整數.

<3> FloatField
       一個浮點數. 必須 提供兩個參數:

       參數    描述
       max_digits    總位數(不包括小數點和符號)
       decimal_places    小數位數
               舉例來講, 要保存最大值爲 999 (小數點後保存2位),你要這樣定義字段:

               models.FloatField(..., max_digits=5, decimal_places=2)
               要保存最大值一百萬(小數點後保存10位)的話,你要這樣定義:

               models.FloatField(..., max_digits=19, decimal_places=10)
               admin 用一個文本框(<input type="text">)表示該字段保存的數據.

<4> AutoField
       一個 IntegerField, 添加記錄時它會自動增加. 你一般不須要直接使用這個字段;
       自定義一個主鍵:my_id=models.AutoField(primary_key=True)
       若是你不指定主鍵的話,系統會自動添加一個主鍵字段到你的 model.

<5> BooleanField
       A true/false field. admin 用 checkbox 來表示此類字段.

<6> TextField
       一個容量很大的文本字段.
       admin 用一個 <textarea> (文本區域)表示該字段數據.(一個多行編輯框).

<7> EmailField
       一個帶有檢查Email合法性的 CharField,不接受 maxlength 參數.

<8> DateField
       一個日期字段. 共有下列額外的可選參數:
       Argument    描述
       auto_now    當對象被保存時,自動將該字段的值設置爲當前時間.一般用於表示 "last-modified" 時間戳.
       auto_now_add    當對象首次被建立時,自動將該字段的值設置爲當前時間.一般用於表示對象建立時間.
       (僅僅在admin中有意義...)

<9> DateTimeField
        一個日期時間字段. 相似 DateField 支持一樣的附加選項.

<10> ImageField
       相似 FileField, 不過要校驗上傳對象是不是一個合法圖片.#它有兩個可選參數:height_field和width_field,
       若是提供這兩個參數,則圖片將按提供的高度和寬度規格保存.    
<11> FileField
    一個文件上傳字段.
    要求一個必須有的參數: upload_to, 一個用於保存上載文件的本地文件系統路徑. 這個路徑必須包含 strftime #formatting,
    該格式將被上載文件的 date/time
    替換(so that uploaded files don't fill up the given directory).
    admin 用一個<input type="file">部件表示該字段保存的數據(一個文件上傳部件) .

    注意:在一個 model 中使用 FileField 或 ImageField 須要如下步驟:
           (1)在你的 settings 文件中, 定義一個完整路徑給 MEDIA_ROOT 以便讓 Django在此處保存上傳文件.
           (出於性能考慮,這些文件並不保存到數據庫.) 定義MEDIA_URL 做爲該目錄的公共 URL. 要確保該目錄對
            WEB服務器用戶賬號是可寫的.
           (2) 在你的 model 中添加 FileField 或 ImageField, 並確保定義了 upload_to 選項,以告訴 Django
            使用 MEDIA_ROOT 的哪一個子目錄保存上傳文件.你的數據庫中要保存的只是文件的路徑(相對於 MEDIA_ROOT).
            出於習慣你必定很想使用 Django 提供的 get_<#fieldname>_url 函數.舉例來講,若是你的 ImageField
            叫做 mug_shot, 你就能夠在模板中以 {{ object.#get_mug_shot_url }} 這樣的方式獲得圖像的絕對路徑.

<12> URLField
     用於保存 URL. 若 verify_exists 參數爲 True (默認), 給定的 URL 會預先檢查是否存在( 即URL是否被有效裝入且
     沒有返回404響應).
     admin 用一個 <input type="text"> 文本框表示該字段保存的數據(一個單行編輯框)

<13> NullBooleanField
      相似 BooleanField, 不過容許 NULL 做爲其中一個選項. 推薦使用這個字段而不要用 BooleanField 加 null=True 選項
      admin 用一個選擇框 <select> (三個可選擇的值: "Unknown", "Yes""No" ) 來表示這種字段數據.

<14> SlugField
      "Slug" 是一個報紙術語. slug 是某個東西的小小標記(短籤), 只包含字母,數字,下劃線和連字符.#它們一般用於URLs
      若你使用 Django 開發版本,你能夠指定 maxlength. 若 maxlength 未指定, Django 會使用默認長度: 50.  #
      之前的 Django 版本,沒有任何辦法改變50 這個長度.
      這暗示了 db_index=True.
      它接受一個額外的參數: prepopulate_from, which is a list of fields from which to auto-#populate
      the slug, via JavaScript,in the object's admin form: models.SlugField
      (prepopulate_from=("pre_name", "name"))prepopulate_from 不接受 DateTimeFields.

<15> XMLField
       一個校驗值是否爲合法XML的 TextField,必須提供參數: schema_path, 它是一個用來校驗文本的 RelaxNG schema #的文件系統路徑.

<16> FilePathField
       可選項目爲某個特定目錄下的文件名. 支持三個特殊的參數, 其中第一個是必須提供的.
       參數    描述
       path    必需參數. 一個目錄的絕對文件系統路徑. FilePathField 據此獲得可選項目.
       Example: "/home/images".
       match    可選參數. 一個正則表達式, 做爲一個字符串, FilePathField 將使用它過濾文件名.
       注意這個正則表達式只會應用到 base filename 而不是
       路徑全名. Example: "foo.*\.txt^", 將匹配文件 foo23.txt 卻不匹配 bar.txt 或 foo23.gif.
       recursive可選參數.要麼 True 要麼 False. 默認值是 False. 是否包括 path 下面的所有子目錄.
       這三個參數能夠同時使用.
       match 僅應用於 base filename, 而不是路徑全名. 那麼,這個例子:
       FilePathField(path="/home/images", match="foo.*", recursive=True)
       ...會匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif

<17> IPAddressField
       一個字符串形式的 IP 地址, (i.e. "24.124.1.30").
<18> CommaSeparatedIntegerField
       用於存放逗號分隔的整數值. 相似 CharField, 必需要有maxlength參數.
更多字段!

更多參數:

(1)null

若是爲True,Django 將用NULL 來在數據庫中存儲空值。 默認值是 False.

(1)blank

若是爲True,該字段容許不填。默認爲False。
要注意,這與 null 不一樣。null純粹是數據庫範疇的,而 blank 是數據驗證範疇的。
若是一個字段的blank=True,表單的驗證將容許該字段是空值。若是字段的blank=False,該字段就是必填的。

(2)default

字段的默認值。能夠是一個值或者可調用對象。若是可調用 ,每有新對象被建立它都會被調用。

(3)primary_key

若是爲True,那麼這個字段就是模型的主鍵。若是你沒有指定任何一個字段的primary_key=True,
Django 就會自動添加一個IntegerField字段作爲主鍵,因此除非你想覆蓋默認的主鍵行爲,
不然不必設置任何一個字段的primary_key=True。

(4)unique

若是該值設置爲 True, 這個數據字段的值在整張表中必須是惟一的

(5)choices
由二元組組成的一個可迭代對象(例如,列表或元組),用來給字段提供選擇項。 若是設置了choices ,默認的表單將是一個選擇框而不是標準的文本框,<br>並且這個選擇框的選項就是choices 中的選項。
更多參數

終於搞完上面這部分,頭都暈了,如今開始數據庫的增刪改查

2.2 ORM的增刪改查

2.2.1 ORM的增長

ORM增長其實就是類的實例化對象,而後添加進去

from app01.models import Book

# 第一種實例化對象
def add_obj(request):
    # 實例化一個對象,可是必須加一個對象的save方法,才能同步
    book_obj = Book(title="孤獨九劍",price=180,publish='華山出版社',pub_data='2019-1-12')
    book_obj.save()


# 第二種實例化方法
#     實例化一個對象,同時直接同步到數據庫中
    book=Book.objects.create(title="葵花寶典",price=200,publish='明教出版社',pub_data='2019-1-14')
    book=Book.objects.create(title="少林五棍",price=220,publish='少林出版社',pub_data='2019-1-20')
    book1=Book.objects.create(title="繡花針",price=190,publish='明教出版社',pub_data='2019-1-18')

    return HttpResponse("新增成功!")

2.2.2 ORM的查詢

ORM查詢就是在python中使用SQL的查詢語句

按照查詢獲取到的數據類型,整理以下

#返回結果爲QuerySet列表

1.all() #返回全部的數據以及對象
2.filter() #返回過濾知足條件的對象列表
3.order_by() #默認是升序,若是在條件前加入- 排序方式就更改成降序
4.values() #返回一個相似字典的的對象
5.reverse() #對對象排序進行反轉
6.distinct() #去重,須要注意的是去重是去的重複的對象,單表就沒法去重。
7.values_list() #生成一個相似的元祖
8.exclude() #跟filter相反,filter前面加上not和exclude()效果一致


#返回對象
1.get() 多個結果和沒有結果會報錯,有且只有一個才能夠
2.first() 獲取到最開始的一個
3.last() 獲取到最後一個


#返回數據類型
1. exists() 返回布爾值,判斷是一個表是不是空表
2. count() 返回int計數。

在這種指定對象的查詢以外,還有一些是模糊查詢

#全部的模糊查詢

Book.objects.filter(price__in=[100,200,300])#雙下方法之在列表內的數據

Book.objects.filter(price__gt=100)#雙下方法之大於指定的值,gte爲大於等於

Book.objects.filter(price__lt=100)#雙下方法之小於指定的值,ite爲小於等於

Book.objects.filter(price__range=[100,200])#雙下方法之在100到200的範圍,首尾都包含

Book.objects.filter(title__contains="python")#雙下方法之包含

Book.objects.filter(title__icontains="python")#雙下方法之非,不區分大小寫

Book.objects.filter(title__startswith="py")#雙下方法之以什麼開始,區分大小寫

Book.objects.filter(pub_date__year=2012)#雙下方法之判斷時間,例如年爲2012

2.2.3 ORM的刪除

(1)流程以下圖

1552624686590

(2)刪除表記錄

刪除方法就是 delete()。它運行時當即刪除對象而不返回任何值。例如:

model_obj.delete()

也能夠一次性刪除多個對象。每一個 QuerySet 都有一個 delete() 方法,它一次性刪除 QuerySet 中全部的對象。

Entry.objects.filter(pub_date__year=2005).delete()

在 Django 刪除對象時,會模仿 SQL 約束 ON DELETE CASCADE 的行爲,換句話說,刪除一個對象時也會刪除與它相關聯的外鍵對象。例如:

b = Blog.objects.get(pk=1) # This will delete the Blog and all of its Entry objects. b.delete()

要注意的是: delete() 方法是 QuerySet 上的方法,但並不適用於 Manager 自己。這是一種保護機制,是爲了不意外地調用 Entry.objects.delete() 方法致使 全部的 記錄被誤刪除。若是你確認要刪除全部的對象,那麼你必須顯式地調用:

Entry.objects.all().delete()
#若是不想級聯刪除,能夠設置爲:
pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True)

2.2.4 ORM的修改

對於ORM的修改能夠有倆種方式

#第一種方式
Book.objects.filter(title__startswith="py").update(price=120)
不過若是是經過對象來修改屬性,最後要save。
對於objects,則是使用提供的方法直接update更新
#第二種方式
先拿到一個dict,而後對對象傳遞整個字典
models.Book.objects.filter(pk=book_id).update(**data)  # name和數據庫字段保持一致

解釋下對於先後端的修改或者新增的大體過程

1552617211012

1552617443761

 

3. 多表操做

上面的操做都是基於單表來操做,能夠對於一個業務邏輯來講,單表是最單一的,最後都會歸根於多表操做

3.1 建立模型

實例:咱們來假定下面這些概念,字段和關係
做者模型:一個做者有姓名和年齡。
做者詳細模型:把做者的詳情放到詳情表,包含生日,手機號,家庭住址等信息。做者詳情模型和做者模型之間是一對一的關係(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",on_delete=models.CASCADE)

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

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=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
    # 與Author表創建多對多的關係,ManyToManyField能夠建在兩個模型中的任意一個,自動建立第三張表
    authors=models.ManyToManyField(to='Author',)

生成表以下:





注意事項:

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

3.2 添加數據

添加數據要不直接在數據庫中添加數據,要不就是在代碼中添加對象

補充 class RelatedManager  關係選擇器,單獨一節

3.3 基於對象的跨表查詢

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

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

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

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

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

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

# 查詢 人民出版社出版過的全部書籍

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

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

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

一對多查詢

# 練習1:  查詢蘋果出版社出版過的全部書籍的名字與價格(一對多)
     # 正向查詢 按字段:publish
     queryResult=Book.objects
            .filter(publish__name="蘋果出版社")
            .values_list("title","price")
     # 反向查詢 按表名:book
     queryResult=Publish.objects
              .filter(name="蘋果出版社")
              .values_list("book__title","book__price")

多對多查詢 

# 練習2: 查詢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")

混合使用

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

    # 正向查詢
    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")

# 練習4: 手機號以151開頭的做者出版過的全部書籍名稱以及出版社名稱

    queryResult=Book.objects
            .filter(authors__authorDetail__telephone__regex="151")
            .values_list("title","publish__name")

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

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

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

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

queryResult=Publish.objects
          .filter(name="人民出版社")
          .values_list("bookList__title","bookList__price")
注意:正向查詢按字段,反向查詢按小寫表名

4.聚合查詢與分組查詢

4.1 聚合查詢

聚合 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, Max, Min
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

4.2 分組查詢

需求以下:
###################################--單表分組查詢--#######################################################
查詢每個部門名稱以及對應的員工數
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.all().values("dep").annotate(Count("id")
###################################--多表分組查詢--#######################################################
多表分組查詢:
查詢每個部門名稱以及對應的員工數
emp:
id  name age   salary   dep_id
1   alex  12   2000       1
2   egon  22   3000       2
3   wen   22   5000       2
dep
id   name
1    銷售部
2    人事部
emp-dep:
id  name age   salary   dep_id   id   name
1   alex  12   2000       1      1    銷售部
2   egon  22   3000       2      2    人事部
3   wen   22   5000       2      2    人事部
sql語句:
select dep.name,Count(*) from emp left join dep on emp.dep_id=dep.id group by emp.dep_id
select dep.name,Count(*) from emp left join dep on emp.dep_id=dep.id group by dep.id,dep.name
ORM:
dep.objetcs.all().annotate(c=Count("emp")).values("name","c")
需求
 

annotate()爲調用的QuerySet中每個對象都生成一個獨立的統計值(統計方法用聚合函數)。 
(1) 練習:統計每一本書的做者個數

 
bookList=Book.objects.annotate(authorsNum=Count('authors'))
for book_obj in bookList:
    print(book_obj.title,book_obj.authorsNum)

###################################################
SELECT
"app01_book"."nid",
"app01_book"."title",
"app01_book"."publishDate",
"app01_book"."price",
"app01_book"."pageNum",
"app01_book"."publish_id",
COUNT("app01_book_authors"."author_id") AS "authorsNum"
FROM "app01_book" LEFT OUTER JOIN "app01_book_authors"
ON ("app01_book"."nid" = "app01_book_authors"."book_id")
GROUP BY
"app01_book"."nid",
"app01_book"."title",
"app01_book"."publishDate",
"app01_book"."price",
"app01_book"."pageNum",
"app01_book"."publish_id"
 

(2) 若是想對所查詢對象的關聯對象進行聚合:
練習:統計每個出版社的最便宜的書

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:

queryResult=Book.objects.values("publish__name").annotate(MinPrice=Min('price'))

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

5.F查詢與Q查詢

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

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

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

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

a

相關文章
相關標籤/搜索