Django ORM 多表操作

HammerZe發表於2022-03-03

Django ORM 多表操作

表模型

  • 圖書表
  • 出版社表
  • 作者表
  • 作者詳情表

表關係

  • 一對一:一對一推薦建在查詢頻率高的一方
  • 一對多:外來鍵欄位建在多的一方
  • 多對多:外來鍵欄位建在查詢頻率多的一方,在Django第三張表不需要建立,自動建立

ps:外來鍵欄位不需要寫表名_id後面的_id,ORM建立的時候自動新增了_id,以及外來鍵以虛擬欄位的形式存在

建立模型

'''models.py'''
from django.db import models
# 書籍表
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    pub_date = models.DateField()
    publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')


# 出版社表
class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)


# 作者表
class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE)


# 作者詳情表
class AuthorDetail(models.Model):
    phone_number = models.BigIntegerField()
    email = models.EmailField()

說明:

  • 1、EmailField 資料型別是郵箱格式,底層繼承 CharField,進行了封裝,相當於 MySQL 中的 varchar。
  • 2、Django1.1 版本不需要聯級刪除:on_delete=models.CASCADE,Django2.2 需要。
  • 3、一般不需要設定聯級更新.
  • 4、外來鍵在一對多的多中設定:models.ForeignKey("關聯類名", on_delete=models.CASCADE)
  • 5、OneToOneField = ForeignKey(...,unique=True)設定一對一。
  • 6、若有模型類存在外來鍵,建立資料時,要先建立外來鍵關聯的模型類的資料,不然建立包含外來鍵的模型類的資料時,外來鍵的關聯模型類的資料會找不到

逆向到表模型

image

插入資料

image

image

image

ps:插入幾條資料方便操作

ORM 新增資料(新增外來鍵)

一對多(外來鍵 ForeignKey)

方式一: 傳物件的形式,返回值的資料型別是物件,書籍物件

步驟:

  • 獲取出版社物件
  • 給書籍的出版社屬性 pulish 傳出版社物件
'''test.py'''
import os
def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoForeignKey.settings')
    import django
    django.setup()
    from app01 import models
    pub_obj = models.Publish.objects.filter(pk=1).first()
    book = models.Book.objects.create(title='水滸傳',price='99.9',pub_date='2021-02-07',publish=pub_obj)
    print(book)  # Book object (3)
if __name__ == '__main__':
    main()

方式二: 傳物件 id 的形式(常用

一對多中,設定外來鍵屬性的類(多的表)中,MySQL 中顯示的欄位名是:外來鍵屬性名_id

返回值的資料型別是物件,書籍物件。

步驟:

  • a. 獲取出版社物件的 id
  • b. 給書籍的關聯出版社欄位 pulish_id 傳出版社物件的 id
import os

def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoForeignKey.settings')
    import django
    django.setup()
    from app01 import models
    book = models.Book.objects.create(title='西遊記',price='88.8',pub_date='2008-08-09',publish_id=1)
    print(book)

一對一 (OneToOneFeild)

方式一:傳物件 id 的形式(常用)

格式:外來鍵屬性名_id

import os
def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoForeignKey.settings')
    import django
    django.setup()
    from app01 import models
	author = models.Author.objects.create(name='杜甫',age=38,author_detail_id=2)
    print(author)    

方式二:傳物件的形式,返回值的資料型別是物件

import os

def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoForeignKey.settings')
    import django
    django.setup()
    from app01 import models
	author_detail_obj = models.AuthorDetail.objects.filter(pk=3).first()
    author = models.Author.objects.create(name='王羲之',age=44,author_detail=author_detail_obj)
    print(author)

多對多(ManyToManyField):在第三張關係表中新增資料

方式一: 傳物件形式,無返回值。

步驟:

  • 獲取作者物件
  • 獲取書籍物件
  • 給書籍物件的 authors 屬性用 add 方法傳作者物件
import os
def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoForeignKey.settings')
    import django
    django.setup()
    from app01 import models   
    '''多對多'''
    # 獲取作者物件
    author_obj1 = models.Author.objects.filter(pk=4).first()
    author_obj2 = models.Author.objects.filter(pk=5).first()
    # 獲取書籍物件
    book_obj1 = models.Book.objects.filter(pk=3).first()
    book_obj2 = models.Book.objects.filter(pk=3).first()
    # 新增資料
    book_obj1.authors.add(author_obj1,author_obj2)
    book_obj2.authors.add(author_obj2)

image

方式二: 傳物件id形式,無返回值。

步驟:

  • a. 獲取作者物件的 id
  • b. 獲取書籍物件
  • c. 給書籍物件的 authors 屬性用 add 方法傳作者物件的 id
import os

# Create your tests here.
def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoForeignKey.settings')
    import django
    django.setup()
    from app01 import models
	book_obj = models.Book.objects.filter(title='水滸傳').first()
    book_obj.authors.add(1,4)

image

ORM 修改資料

'''一對多,一對一'''
models.Book.objects.filter(pk=1).update(publish_id=2)
book_obj = models.Book.objects.filter(pk=1).first()
print(book_obj.publish) 
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.filter(pk=1).update(publish=publish_obj)
'''多對多'''
book_obj.authors.set([3, 2])
book_obj.authors.set([author_obj1, author_obj2])

ORM 刪除和清空資料

remove():從關聯物件集中移除執行的模型物件

對於 ForeignKey 物件,這個方法僅在 null=True(可以為空)時存在,無返回值

author_obj =models.Author.objects.get(id=1)
book_obj = models.Book.objects.get(id=11)
author_obj.book_set.remove(book_obj)
return HttpResponse("ok")

clear():從關聯物件集中移除一切物件,刪除關聯,不會刪除物件

對於 ForeignKey 物件,這個方法僅在 null=True(可以為空)時存在

#  清空三國關聯的所有作者
book = models.Book.objects.filter(title="三國演義").first()
book.authors.clear()

跨表查詢

  • 正向:外來鍵欄位在哪,去從該表的角度查另外的表,就是正向
  • 反向:從沒有外來鍵表去查另外相關聯的表,就是反向

口訣:

  • 正向查詢按外來鍵欄位
  • 反向查詢按表名小寫

注意:

正向:屬性名
反向:小寫類名加 _set
反向查詢的時候查詢的物件可能有多個的情況加__set,查詢的物件只有一個的情況不需要加

基於物件的跨表查詢

'''子查詢思路'''
1、先查出一個物件
2、基於物件正反查

 '''基於物件的跨表查詢:正向'''
1.查詢三國演義書籍對應的出版社名稱
    book_obj = models.Book.objects.filter(title='三國演義').first()
    publish = book_obj.publish
    print(publish.name)  # 南方出版社
    
2.查詢水滸傳對應的作者
    book_obj = models.Book.objects.filter(title ='水滸傳').first()
    author = book_obj.authors
    print(author.name)
3.查詢李白的手機號
	author_obj = models.Author.objects.filter(name='李白').first()
    author_obj_det = author_obj.author_detail
    print(author_obj_det.phone_number)
   
'''基於物件的跨表查詢:反向'''
4.查詢東方出版社出版過的書籍
    publish = models.Publish.objects.filter(name='東方出版社').first()
    books = publish.book_set.all()
    print(books.first().title) # 西遊記
    
5.查詢杜甫寫過的書
	author = models.Author.objects.filter(name='杜甫').first()
    books = author.book_set.all()
    print(books.first().title)
    
6.查詢email是163@qq.com的作者姓名  # 一對一的情況,不需要寫_set
	author_det = models.AuthorDetail.objects.filter(email='163@qq.com').first()
    author = author_det.author
    print(author.name)  # 李白	

基於雙下劃線的跨表查詢

上面的操作類似子查詢,將上一個查詢結果當作該次查詢的條件使用,那麼現在演示的跨表操作,就類似sql連表操作(inner join···),是兩種不同的查詢方法

 '''基於雙下劃線的跨表操作:連表操作'''
    '''正向'''
1.查詢三國演義書籍對應的出版社名稱
	publish = models.Book.objects.filter(title='三國演義').values('publish__name')
    print(publish)  # <QuerySet [{'publish__name': '南方出版社'}]>
2.查詢水滸傳對應的作者
   author = models.Book.objects.filter(title='水滸傳').values('authors__name','authors__age')
    print(author)
3.查詢李白的手機號
	auuth_det_obj = models.Author.objects.filter(name='李白').values('author_detail__phone_number')
    print(auuth_det_obj)
    
   	'''反向'''
4.查詢東方出版社出版過的書籍
 	book_obj = models.Publish.objects.filter(name='東方出版社').values('book__title')
    print(book_obj)
5.查詢杜甫寫過的書
	books = models.Author.objects.filter(name='杜甫').values('book__title')
    print(books)
6.查詢email是163@qq.com的作者姓名
    author = models.AuthorDetail.objects.filter(email='163@qq.com').values('author__name')
    print(author)

    '''一對一的情況'''
7. 查詢水滸傳對應的作者的電話和電話
    author_det = models.Book.objects.filter(title='水滸傳').values('authors__author_detail__phone_number','authors__author_detail__email')
    print(author_det)

聚合查詢

  • 聚合函式:Sum,Max,Min,Count,Avg
  • 在Django中使用聚合函式需要匯入:from django.db.models import Sum,Max,Min,Min,Count,Avg

注意:

  1. 使用聚合函式查詢的時候,建議把聚合結果欄位重新命名
# 格式:aggregate(別名 = 聚合函式名("屬性名稱"))
models.Book.objects.all().aggregate(AllPrice=Sum('price'))
  1. 聚合查詢返回值的資料型別是字典
  2. 聚合函式 aggregate() 是 QuerySet 的一個終止子句, 生成的一個彙總值,相當於 count(),使用 aggregate() 後,資料型別就變為字典,不能再使用 QuerySet 資料型別的一些 API 了
  3. 日期資料型別(DateField)可以用 Max 和 Min

示例

'''聚合查詢'''
from django.db.models import Avg,Max,Min,Sum,Count
	'''1、查詢所有書籍的總價,平均值,最大值,最小值,個數'''
    book_totalprice = models.Book.objects.all().aggregate(totalprice = Sum('price'),maxprice = Max('price'),minprice = Min('price'),avgprice = Avg('price'),num = Count('price'))
    print(book_totalprice)  
    # {'totalprice': Decimal('377.50'), 'maxprice': Decimal('100.00'), 'minprice': Decimal('88.80'), 'avgprice': Decimal('94.375000'), 'num': 4}

F查詢

F 查詢:取出某個欄位對應的值

匯入:from django.db.models import F

比如F(‘price’)的意思就是取出該書價格的數值

from django.db.models import F
# 每本書的價格上調10元
res = models.Book.objects.update(price=F('price')+10)
    print(res)  # 影響的行數:4

# 查詢評論數大於閱讀數的書籍
    res=models.Book.objects.filter(commit_num__gt=F('read_num'))
    print(res)

Q查詢

我們知道在filter中寫的條件可以以逗號隔開是and的關係,那麼或,非得關係如果構造呢?

此時就需要Q函式來幫助我們去構造這些關係:與(&)或(|)非(~)

匯入: from django.db.models import Q

from django.db.models import Q
1、查詢名字叫三國演義或者價格大於100的書
res = models.Book.objects.filter(Q(title='三國演義')|Q(price__gt=100))
print(res)

2、查詢名字叫三國演義並且價格小於100的書
res = models.Book.objects.filter(Q(title='三國演義')&Q(price__lt=100))
print(res)

3、查詢名字不是三國演義的書名
res = models.Book.objects.filter(~Q(title='三國演義'))
print(res)

相關文章