Django(多表查詢操作)


首先了解一下 mysql中的表之間的關系,一對一,一對多,多對一,多對多。

一對多關系、多對一關系、一對一關系

  1. 至少都有一側是單個實體,所以記錄之間的聯系通過外鍵實現,讓外鍵指向這個實體。
  2. 實現這種關系時,要在“多”這一側加入一個外鍵,指向“一”這一側聯接的記錄。

多對多關系

  1. 解決方法是添加第三個表,這個表稱為關聯表。
  2. 多對多關系可以分解成原表和關聯表之間的兩個一對多關系

多對多關系例子

查詢多對多關系要分成兩步。

  1. 若想知道某位學生選擇了哪些課程,要先從學生和注冊之間的一對多關系開始, 獲取這位學生在 registrations 表中的所有記錄。
  2. 然后再按照多到一的方向遍歷課程和注冊之間的一對多關系, 找到這位學生在 registrations 表中各記錄所對應的課程。
  3. 同樣,若想找到選擇了某門課程的所有學生,你要先從課程表中開始,獲取其在 registrations 表中的記錄,再獲取這些記錄聯接的學生。

自引用關系也是多對多的一種特殊情況

如果關系中的兩側都在同一個表中, 這種關系稱為自引用關系。在關注中, 關系的左側是用戶實體,可以稱為“關注者”;關系的右側也是用戶實體,但這些是“被關注者”。

創建一個多表之間關系的例子

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

添加記錄

#方式一:如果是這樣直接指定publish_id字段去添加值,前提是你的主表里面必須有數據
# 主表:沒有被關聯的(因為book表是要依賴於publish這個表的)也就是publish表
# 子表:關聯的表
book_obj = Book.objects.create(title='小豬豬', publishDate='2012-12-12', price=200, publish_id=1)
#方式二 建議
pub_obj = Publish.objects.filter(name='西瓜出版社')[0])
Book.objects.create(title='神雕俠侶',publishDate='2013-12-12',price=188,publish=pub_obj)
#第二種方法可以避免出版社不存在的問題

多對多添加記錄:

  書和作者是多對多的關系:一個書可以有多個作者,一個作者可以出版多本書

  步驟:先找到書對象

     再找到需要的作者對象

     給書對象綁定作者對象(用add方法),也就是綁定多對多的關系.

#方式一
#
查找作者id xiaojiu =Author.objects.filter(name='xiaojiu').first() zhang = Author.objects.filter(name='zhang').first() #給書籍綁定作者 book_obj.authors.add(zhang,xiaojiu)

#方式二

  pub_obj = Publish.objects.filter(name='蘋果出版社').first()
  book_obj = Book.objects.create(title='蘋果書',publishDate='2012-2-2',price=188,publish=pub_obj)
  authers = Author.objects.all()

  綁定多對多的關系
  book_obj.authors.add(*authers)

  

解除綁定:remove

 # 將某個特定的對象從被關聯對象集合中去除。    ======   book_obj.authors.remove(*[])

    book_obj = Book.objects.filter(title='小豬豬5').last() #找到對象
    authers = Author.objects.filter(nid__lt=3)#找到符合作者的對象
    book_obj.authors.remove(*authers) #因為是多個所以要加*

清除綁定:clear”  #清空被關聯對象集合。

  book_obj = Book.objects.filter(title='小豬豬4')#找到對象
    for book_obj_item in book_obj: #循環賦值
        book_obj_item.authors.clear()#清空關聯對象

總結:remove和clear的區別
  remove:得吧你要清除的數據篩選出來,然后移除
  clear:不用查,直接就把數據都清空了。
  各有應用場景

 

一對一查詢

author和authordetile是一對一的關系

正向查詢(按字段author)

反向查詢(按表名authordeital):因為是一對一的關系了,就不用_set了。

    #一對一查詢
    #正向查詢: 手機號為110的作者姓名
    deital_obj = AuthorDetail.objects.filter(telephone=110).first()
    print(deital_obj.author.name)

    #反向查詢:查詢作者的手機號
    xiaojiu_obj = Author.objects.filter(name='xiaojiu').first()
    print(dir(xiaojiu_obj)) #如果找不到方法,用dir 看看這個對象都有什么方法
    print(xiaojiu_obj.authorDetail.telephone)

一對多查詢

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

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

    #正向查詢:查詢 小紅帽這本書的出版社地址
    book_obj = Book.objects.filter(title='小紅帽')[0] #拿到書對象
    print('=======',book_obj.publish)
    print(book_obj.publish.city)

    #反向查詢:查詢蘋果出版社都初版過哪本書和價格
    pub_obj = Publish.objects.filter(name='蘋果出版社')[0]
    book_dic = pub_obj.book_set.all().values('price','title')[0]
    print(book_dic)
    print(book_dic['price'])
    #查詢出版過的所有書籍
    book_list = pub_obj.book_set.all()
    for book_obj in book_list:
        print(book_obj.title,book_obj.price)

庄傑大佬解答了我的疑惑, 在這里謝謝大佬,小弟甘拜下風

多對多查詢

正向查詢(按字段authorlist)

反向查詢(按表名book_set)

    #多對多查詢
    #正向查詢:查詢小紅帽這本書的所有作者的姓名和年齡
    book_obj = Book.objects.filter(title='小豬豬3')[0]
    print(book_obj.authors.all().values('name','age'))

    #反向查詢 : 查詢作者是xiaojiu的 出了那幾本書
    #方法一: 使用book_set.all

    xiaojiu_obj = Author.objects.filter(name='zhang').first()
    alls= (xiaojiu_obj.book_set.all())
    for i in alls:
        print(i.title)
    #方法二:使用values
    author_list = xiaojiu_obj.book_set.all().values('title')
    for i in author_list:
        print(i.get('title'))

 你可以通過在 ForeignKey() 和ManyToManyField的定義中設置 related_name 的值來覆寫 FOO_set 的名稱。例如,如果 Article model 中做一下更改: publish = ForeignKey(Blog, related_name='bookList'),那么接下來就會如我們看到這般

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

 

基於雙下划線的查詢

Django 還提供了一種直觀而高效的方式在查詢(lookups)中表示關聯關系,它能自動確認 SQL JOIN 聯系。要做跨關系查詢,就使用兩個下划線來鏈接模型(model)間關聯字段的名稱,直到最終鏈接到你想要的 model 為止。(相當於用sql語句用join連接的方式,可以在settings里面設置,可查看sql語句)

一對多查詢

#練習1、蘋果出版社出版過的所有的書的價格和名字
   
    #基於雙下划線的查詢方式
    #第一種查法
    ret = Publish.objects.filter(name='蘋果出版社').values('book__title','book__price')
    # #先找到蘋果出版社這個對象,之后再通過vlaues取值,book表下的什么字段中間用__鏈接,固定寫法
    print(ret)
    #第二種查法
    #對比第一種方法,這種方法就是先join publish表,之后再進行取值。
    ret2 = Book.objects.filter(publish__name='蘋果出版社').values('title','price')
    print(ret2)
  #練習2、查詢手機號以11開頭的作者出版過的所有書的名稱以及出版社的名稱

 

    #方式1
    author_obj = AuthorDetail.objects.filter(telephone__startswith='11').first()#找到以11開頭的電話號,獲取AuthorDetail 主鍵對象

    print(author_obj.author.book_set.all().values('title','publish__name'))#獲取該條件所對應的作者,獲取所有書籍對象信息,通過values取值。
    #方式2

    ret =Book.objects.filter(authors__authorDetail__telephone__startswith='11').values('title','publish__name')
    print(ret)

 

聚合查詢與分組查詢(很重要!!!)

聚合查詢:aggregate(*args, **kwargs),只對一個組進行聚合

#查詢 所有書籍價格的平均值
    print(Book.objects.all().aggregate(Avg('price')))

aggregate()QuerySet 的一個終止子句(也就是返回的不再是一個QuerySet集合的時候),意思是說,它返回一個包含一些鍵值對的字典。鍵的名稱是聚合值的標識符,值是計算出來的聚合值。鍵的名稱是按照字段和聚合函數的名稱自動生成出來的。如果你想要為聚合值指定一個名稱,可以向聚合子句提供它。

    print(Book.objects.all().aggregate(avgprice =Avg('price')))

如果你希望生成不止一個聚合,你可以向aggregate()子句中添加另一個參數。所以,如果你也想知道所有圖書價格的最大值和最小值,可以這樣查詢:

print(Book.objects.all().aggregate(Avg('price'),Max("price"),Min("price")))

#輸出
{'price__avg': 186.222222, 'price__max': Decimal('200.00'), 'price__min': Decimal('100.00')}

分組查詢 :

annotate():為QuerySet中每一個對象都生成一個獨立的匯總值。

 是對分組完之后的結果進行的聚合

    #統計每一本書的作者數量
    #方式一
    print(Book.objects.all().annotate(authorNum = Count('authors__name')).values('authorNum'))
    #方式二
    book_list = Book.objects.all().annotate(authorNum = Count('authors__name'))
    for book_obj in book_list:
        print(book_obj.title,book_obj.authorNum)

#統計每一個出版社最便宜的書

  #方式一
    print(Book.objects.values('publish__name').annotate(MinPrice=Min('price')))

    #方式二
    print(Publish.objects.all().annotate(minprice=Min('book__price')).values('name','minprice'))

    #方式三
    publishlist = Publish.objects.annotate(minprice =Min('book__price'))
    for pub_obj  in publishlist:
        print(pub_obj.name,pub_obj.minprice)

#打印以py開頭的書籍對應的作者個數

   print(Book.objects.filter(title__startswith='py').annotate(au_count = Count('authors__name')).values('au_count'))
#根據一本書的作者數量多少對查詢集QuuerySet進行排序
    print(Book.objects.all().annotate(Num=Count('authors__name')).order_by('Num').values('title'))
  #獲取book所有對象,創建分組Num 統計作者的數量,通過order_by進行排序, values取值

   #查詢各個作者出的書的總價格

#方式一
print(Author.objects.all().annotate(pricesum = Sum('book__price')).values('name','pricesum'))
#方式二
print(Book.objects.values('authors__name').annotate(pricesum = Sum('price')).values('authors__name','pricesum'))

 

 

F與Q查詢

 

F查詢:

在上面所有的例子中,我們構造的過濾器都只是將字段值與某個常量做比較。如果我們要對兩個字段的值做比較,那該怎么做呢?

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

 

#查詢書籍價格小於100元並且作者年齡=2017的書

print(Book.objects.filter(Q(price__lt='100')&Q(authors__authorDetail__birthday__year=2017)).values('title'))

#修改也可以使用F函數,比如說 書籍ID為4的書籍價格上漲100元

 print(Book.objects.filter(nid=4).update(price=F('price')+100))

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

#查詢書記價格小於作者年齡*2的書籍

    print(Book.objects.filter(price__lt=F('authors__age')*2).values('title'))

 

Q查詢:

filter() 等方法中的關鍵字參數查詢都是一起進行“AND” 的。 如果你需要執行更復雜的查詢(例如OR 語句),你可以使用對象

 

 

# 查詢書籍價格小於100元並且作者年份等於2017的書

print(Book.objects.filter(Q(price__lt='100')&Q(authors__authorDetail__birthday__year=2017)).values('title'))

#查詢評論數大於100或者閱讀數小於200的書

print(models.Book.objects.filter(Q(commentNum__gt=100)|Q(readNum__lt=200)))
Q 對象可以使用& 和| 操作符組合起來。當一個操作符在兩個Q 對象上使用時,它產生一個新的Q 對象。

#查詢年份等於2017年或者價格大於200的書

 print(models.Book.objects.filter(Q(publishDdata__year=2017)|Q(price__gt=200)))

#查詢年份不是2017年或者價格大於200的書

print(models.Book.objects.filter(~Q(publishDdata__year=2017)&Q(price__gt=200)))

 

與:&Q

或:|Q

非:~Q

 

 

 

注意:

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

 

查詢函數可以混合使用Q 對象和關鍵字參數。所有提供給查詢函數的參數(關鍵字參數或Q 對象)都將"AND”在一起。但是,如果出現Q 對象,它必須位於所有關鍵字參數的前面。例如:


免責聲明!

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



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