Django ORM 多表查詢示例代碼

數據準備

moduls.py

# 構建表結構
from django.db import models
 
# 表app01_publish
class Publish(models.Model):
    name = models.CharField(max_length=20)
    addr = models.CharField(max_length=20)
 
# 表app01_author_detail
class Author_Detail(models.Model):
    tel = models.CharField(max_length=20)
 
# 表app01_author
class Author(models.Model):
    name = models.CharField(max_length=20)
    age = models.IntegerField()
 
    # 表app01_author一對一表app01_authordetail
    detail = models.OneToOneField(to='Author_Detail',to_field='id',unique=True,on_delete=models.CASCADE)
 
# 表app01_book
class Book(models.Model):
    title = models.CharField(max_length=20)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    pub_date = models.DateField(auto_now_add=True)
 
    # 表app01_book多對一表app01_publish,參數to指定模型名,參數to_field指定要關聯的那個字段
    publish = models.ForeignKey(to='Publish',to_field='id',on_delete=models.CASCADE)
 
    # 我們自己寫sql時,針對書籍表與作者表的多對關系,需要自己創建新表,而基於django的orm,下面這一行代碼可以幫我們自動創建那張關系表
    authors=models.ManyToManyField(to='Author')
    # 變量名為authors,則新表名為app01_book_authors,若變量名為xxx,則新表名為app01_book_xxx
 

tests.py

# 添加數據
 
import os
 
if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "book_sys.settings")
    import django
    django.setup()
 
    from app01.models import *
    # 1、先添加沒有外鍵字段的 author_detail表 pubulish表
    Author_Detail.objects.create(tel='123456789')
    Author_Detail.objects.create(tel='987654321')
    Author_Detail.objects.create(tel='000000000')
 
    Publish.objects.create(name='北方出版社',addr='北京')
    Publish.objects.create(name='南方出版社',addr='南京')
    Publish.objects.create(name='東方出版社',addr='上海')
    Publish.objects.create(name='西方出版社',addr='西安')
 
    # 2、添加 author表 book表
    Author.objects.create(name='frank',age=31 ,detail_id=1)
    Author.objects.create(name='lili',age=29 ,detail_id=2)
    Author.objects.create(name='tank',age=42 ,detail_id=3)
 
    Book.objects.create(title='三國演義',price=200 ,publish_id=1)
    Book.objects.create(title='三國志',price=198.5 ,publish_id=2)
    Book.objects.create(title='紅樓夢',price=255.43 ,publish_id=2)
    Book.objects.create(title='西遊記',price=300.5 ,publish_id=3)
    Book.objects.create(title='西廂記',price=213.4 ,publish_id=4)
    Book.objects.create(title='水滸傳',price=199 ,publish_id=1)
 
    # 3、最後操作 author_book表,由於使用的是 ManyToMany 字段自動生成的,所以要基於外鍵所在的表進行操作
    book_obj1=Book.objects.filter(pk=1).first()
    book_obj1.authors.add(1,2)
 
    book_obj2 = Book.objects.filter(pk=2).first()
    book_obj2.authors.add(1)
 
    book_obj3 = Book.objects.filter(pk=3).first()
    author_obj1 = Author.objects.filter(pk=1).first()
    author_obj2 = Author.objects.filter(pk=2).first()
    book_obj3.authors.add(author_obj1,author_obj2)
 
    book_obj4 = Book.objects.filter(pk=4).first()
    book_obj4.authors.add(3,2)
 
    book_obj5 = Book.objects.filter(pk=5).first()
    book_obj5.authors.add(3)
 
    book_obj6 = Book.objects.filter(pk=6).first()
    book_obj6.authors.add(1,3)

正向查詢與反向查詢

一出版社和書籍為例, 書記表含有出版社表的外鍵字段

正向查詢>>> 書籍查詢出版社

反向查詢>>> 出版社查詢書籍

總結: 當前查詢對象是否含有外鍵字段, 有就是正向查詢, 沒有就是反向查詢

正向查詢按照字段查詢, 反向查詢按照表名查詢

基於對象的跨表查詢

相當於MySQL中的子查詢: 將一張表的查詢結果用括號括起來, 當作另一條SQL語句的條件 . 

正向查詢

一對多

查詢書籍主鍵為5 的出版社名稱

1. 查主鍵為5的書籍對象

2. 根據書籍對象的外鍵字段 publish 獲取到出版社對象

3. 由出版社對象獲取到名稱

book_obj = Book.objects.filter(pk=5).first()
res = book_obj.publish
print(res)
# Publish object
print(res.name)
# 西方出版社

多對多

查詢書籍主鍵為3的作者姓名

1. 查詢書籍主鍵為3的書籍對象

2. 外鍵字段在書籍表中, 同樣是正向查詢,那麼隻需要按照字段autjors查詢即可

3. 獲取作者對象的姓名

book_obj = Book.objects.filter(pk=3).first()
res = book_obj.authors
print(res)
# app01.Author.None

註意: 由於字段authors 是多對多的外鍵字段, 此時拿到的對象還需要進一步的操作

book_obj = Book.objects.filter(pk=3).first()
res = book_obj.authors
 
res1 = book_obj.authors.all()
print(res1)
# <QuerySet [<Author: Author object>, <Author: Author object>]>

然後再for循環各自的姓名即可

一對一

查詢作者lili的號碼

1.查詢作者對象

2,外鍵字段再作者表中, 同樣是正向查詢,那麼隻需要按照字典detail查詢即可

3. 獲取詳情對象的tel

author_obj = Author.objects.filter(name='lili').first()
res = author_obj.detail
print(res)
print(res.tel)

反向查詢 一對多

查詢東方出版社出版的書籍

1. 先獲取東方出版社的對象

2. 出版社沒有外鍵字段, 去查書籍是反向查詢

3. 表名小寫_set.all()

4. 獲取書籍名稱

publish_obj=Publish.objects.filter(name='東方出版社').first()
print(publish_obj)
# Publish object
 
res = publish_obj.book_set
print(res)
# app01.Book.None
 
res1 = res.all()
print(res1)
# <QuerySet [<Book: Book object>]>
for obj in res1:
  print(obj.title)

多對多

查詢作者lili寫過的書籍

1.獲取作者對象

2. 作者表中沒有書籍表的外鍵, 所以是反向查詢

3. .book_set.all()獲取書籍對象

4. 再獲取書籍對象的名稱

author_obj = Author.objects.filter(name='lili').first()
res = author_obj.book_set
print(res)
# app01.Book.None
res1 = res.all()
print(res1)
# <QuerySet [<Book: Book object>, <Book: Book object>, <Book: Book object>]>
for obj in res1:
  print(obj.title, end='')
# 三國演義,紅樓夢,西遊記,

一對一

查詢號碼為0000000的作者

1. 查詢作者對象詳情

2.外鍵字段在作者表中, 同樣是證詞昂查詢, 那麼隻需要按照字段detail 查詢即可

3. 獲取詳情對象的tel 

detail_obj = Author_Detail.objects.filter(tel='000000000').first()
print(detail_obj)
# Author_Detail object
res = detail_obj.author
print(res)
# Author object
print(res.name)
# tank

方法總結

正向查詢

一對一, 一對多的方法是一樣的, 按照字段查詢就能夠直接找到對象 — author_obj.detail

一對多 , 按照字段查詢後會返回一個.None的對象, 需要在字段後面加上.all()>>> book_obj.authors.all()

反向查詢

一對多,多對多的方法是一樣的, 按照表名小寫, 在跟上_set.all()>>>author_obj.book_set.all()

一對一, 則直接按照表明小寫, 能夠直接拿到>> > detail_obj.author

當查詢到的結果中最後以.None結尾, 都需要在原來的查詢方法後面在跟上.all()才能獲得想要的結果

基於雙下線的跨表查詢

相當於MySQL中的連表查詢: 將兩張表或者對賬表連成一張表進行查詢

正向查詢

正向查詢, 按照關聯字段+雙下線>>> .values('關聯字段__被關聯表中的字段'), 返回的是一個QuerySet對象

一對一

查詢作者frank的手機號

res = Author.objects.filter(name='frank').values('detail__tel')
print(res)
# <QuerySet [{'detail__tel': '123456789'}]>
 
print(res.first())
# {'detail__tel': '123456789'}

一對多

查詢三國演義的出版社名字

res = Book.objects.filter(title='三國演義').values('publish__name')
print(res)
print(res.first())
 
# <QuerySet [{'publish__name': '北方出版社'}]>
# {'publish__name': '北方出版社'}

多對多

查詢三國演義的所有作者

res = Book.objects.filter(title='三國演義').values('authors__name')
print(res)
# <QuerySet [{'authors__name': 'frank'}, {'authors__name': 'lili'}]>

反向查詢

按模型名(小寫)+雙下線>>> .values('表名小寫__被關聯表中的字段'), 返回的是一個QuerySet對象. 

一對一

查詢手機號為'123456789'的作者名

res = Author_Detail.objects.filter(tel='123456789').values('author__name')
print(res)
# <QuerySet [{'author__name': 'frank'}]>

一對多

查詢北方出版社出版的所有書籍名字

res = Publish.objects.filter(name='北方出版社').values('book__title')
print(res)
# <QuerySet [{'book__title': '三國演義'}, {'book__title': '水滸傳'}]>

多對多

查詢lili出版的所有書籍

res = Author.objects.filter(name='lili').values('book__title')
print(res)
# <QuerySet [{'book__title': '三國演義'}, {'book__title': '紅樓夢'}, {'book__title': '西遊記'}]>

方法總結:

正向查詢,按關聯字段: .values('關聯字段__被關聯表中的字段'), 返回的是一個QuerySet對象

按模型名(小寫)+雙下線: .values('表名小寫__被關聯表中的字段'), 返回的是一個QuerySet對象, 對象中保存的是字典類型的數據

雙下高階正反向查詢

使用filter()的雙下線查詢

首先需要考慮的是正向查詢還是反向查詢, 確定括號內使用的方法, 但是括號外面不是使用values,而是使用filter!!!

註意: 使用filter方法字段是不能加引號的, values需要加引號

查詢書籍主鍵為反向, 使用.filter('表名小寫__被關聯表中的字段')

res = Publish.objects.filter(book__pk= 4)
print(res)
 
# <QuerySet [<Publish: Publish object>]>
res = Publish.objects.filter(book__title= '西遊記')
print(res)
# 結果相同

連續跨多張表查詢

套路與上面的案例都是一樣的, 可以練習n個雙下劃線, 隻需要在每次雙下線的時候, 確定是每個雙下線後面是正向查詢還是反向查詢即可

# 需求1:查詢北京出版社出版過的所有書籍的名字以及作者的姓名、手機號
# 方式一:基表為Publish
res=Publish.objects.filter(name='北方出版社').values_list('book__title','book__authors__name','book__authors__author_detail__tel')
 
# 方式二:基表為Book
res=Book.objects.filter(publish__name='北方出版社').values_list('title','authors__name','authors__author_detail__tel')
 
# 循環打印結果均為
for obj in res:
    print(obj)
 
 
 
# 需求2:查詢手機號以186開頭的作者出版過的所有書籍名稱以及出版社名稱
# 方式一:基表為AuthorDetail
res=AuthorDetail.objects.filter(tel__startswith='186').values_list('author__book__title','author__book__publish__name')
 
# 方式二:基表為Book
res=Book.objects.filter(authors__author_detail__tel__startswith='186').values_list('title','publish__name')
 
# 方式三:基表為Publish
res=Publish.objects.filter(book__authors__author_detail__tel__startswith='186').values_list('book__title','name')
 
# 循環打印結果均為
for obj in res:
    print(obj)
 

到此這篇關於Django ORM 多表查詢的文章就介紹到這瞭,更多相關Django ORM 多表查詢內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: