基于双下划线的跨表查询
1、Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认SQL JOIN联系。要做跨关系查询,就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的model为止
2、基于双下划线的跨表查询,其本质是使用join连接其他表进行查询(连表查询)
3、基于双下划线的跨表查询:如果为正向查询(子表查询主表)则按字段,反向查询时则按表名小写
一对多
⑴编辑模型:Book模型中用于连接Author表的外键名实际为"author_a"这张图在截图后修改过一下,为了方便区分主表模型名小写形式(author)和Book模型中的外键属性名(author->author_a)
⑵查看数据
例1:查询作者是"吴承恩"的所有图书
# -*- coding: utf-8 -*-
from django.http import HttpResponse
from polls.models import Author,Book,Publisher,BookOrder
from django.db import models
from django.db import connection
def index(request):
#查询作者是"吴承恩"的所有图书
#正向查询:外键字段__关联表的字段
result = Book.objects.filter(author_a__name='吴承恩')#这个例子中专门把外键属性名定义来跟主表的模型名不一致,以便区分
print(result)
print(result.query)
#反向查询:表名__字段
result_1 = Author.objects.filter(name='吴承恩').values("book","book__name")
print(result_1)
print(result_1.query)
result_2 = Author.objects.filter(name='吴承恩').values("book")
print(result_2)
print(result_2.query)
return HttpResponse("success")
"""
<QuerySet [<Book: Book object (1)>, <Book: Book object (3)>]>
SELECT `book`.`id`, `book`.`name`, `book`.`pages`, `book`.`price`, `book`.`rating`, `book`.`author_a_id`, `book`.`publisher_a_id`,
`book`.`create_time`, `book`.`update_time` FROM `book` INNER JOIN `author` ON (`book`.`author_a_id` = `author`.`id`) WHERE `author`.`name` = 吴承恩
<QuerySet [{'book__name': '西游记', 'book': 1}, {'book__name': '西游记后传', 'book': 3}]>
SELECT `book`.`id`, `book`.`name` FROM `author` LEFT OUTER JOIN `book`
ON (`author`.`id` = `book`.`author_a_id`) WHERE `author`.`name` = 吴承恩
<QuerySet [{'book': 1}, {'book': 3}]>
SELECT `book`.`id` FROM `author` LEFT OUTER JOIN `book`
ON (`author`.`id` = `book`.`author_a_id`) WHERE `author`.`name` = 吴承恩
"""
例2:查询"西游记"这本书的作者
# -*- coding: utf-8 -*-
from django.http import HttpResponse
from polls.models import Author,Book,Publisher,BookOrder
from django.db import models
from django.db import connection
def index(request):
#查询"西游记"这本书的作者
#正向查询:外键字段__关联表的字段
result = Book.objects.filter(name="西游记").values("author_a__name")
print(result)
print(result.query)
#反向查询:表名__字段
result_1 = Author.objects.filter(book__name='西游记')
print(result_1)
print(result_1.query)
return HttpResponse("success")
"""
<QuerySet [{'author_a__name': '吴承恩'}]>
SELECT `author`.`name` FROM `book` INNER JOIN `author`
ON (`book`.`author_a_id` = `author`.`id`) WHERE `book`.`name` = 西游记
<QuerySet [<Author: Author object (1)>]>
SELECT `author`.`id`, `author`.`name`, `author`.`age`, `author`.`email`
FROM `author` INNER JOIN `book` ON (`author`.`id` = `book`.`author_a_id`) WHERE `book`.`name` = 西游记
"""
注:从上面两个例子可以看出,在进行查询时
1、首先要分清楚最终要查询的数据是在子表中还是主表中
⑴查询作者是"吴承恩"的所有图书:最终查询的是"图书",因此是在子表中(Book模型类中)
⑵查询"西游记"这本书的作者:最终查询的是"作者",因此是在主表中(Author模型类中)
2、确定好查询语句是从哪个表查询到哪个表(主表和子表)
⑴查询作者是"吴承恩"的所有图书(最终查询的语句是在子表中),且查询语句以"Book"模型类开始,那么为"正向查询",方法为:外键字段__关联表的字段 (关联表的字段是可变的:即,需要根据哪个字段来查询就是哪个字段,author_a__name中就是根据主表中的name来查询的)
⑵查询作者是"吴承恩"的所有图书(最终查询的语句是在子表中),且查询语句以"Author"模型类开始,那么为"反向查询",方法为:表名__关联表的字段 (关联表的字段是可变的)
2.1、确定好查询语句是从哪个表查询到哪个表(主表和子表)
⑴查询"西游记"这本书的作者(最终查询的语句是在主表中),且查询语句以"Book"模型类开始,那么为"正向查询",方法为:外键字段__关联表的字段
⑵查询"西游记"这本书的作者(最终查询的语句是在主表中),且查询语句以"Author"模型类开始,那么为"反向查询",方法为:表名__关联表的字段
3、可以看出:
⑴如果最终查询的数据在子表中:那么最好以"子表模型类"开始:使用正向查询,外键字段__关联表的字段
⑵如果最终查询的数据在主表中:那么最好以"主表模型类"开始:使用反向查询,表名__字段
4、values()方法中的第一个参数可以要可以不要:
⑴不要,即直接为values("表名__需要查询的字段名"):这样查询结果就是一个字典型的集合(键为表名,值为数据id值)
⑵要,即为values("表名__需要查询的字段名"):这样查询出来的就是将需要查询的字段的值赋值给"表名__需要查询的字段名"的变量,将数据id值赋值给第一个参数变量(感觉最好还是要有第一个参数比较好些,可以使用表名来作为第一个参数)
基于对象的跨表查询
1、关系属性(字段)写在哪个类(表) 里面,从当前类(表)的数据去查询它关联类(表)的数据叫做正向查询,反之叫做反向查询
2、基于对象的查询是子查询,也就是多次查询
⑴正向查询:正向查询靠对象,取到数据对象后,通过点操作符对外键操作,就能拿到外键的对象,从而取到内容
⑵反向查询:查到对象后,通过小写的表名_set来获取另一个表的属性(注意对象调用的是加_set 的方法,然后all()即可取出全部内容)
3、基于对象查询的特点在于:不管是正向查询还是反向查询,第一步查询出来的数据都只能有一条(使用get()或first()等)
⑴正向查询:先查询子表,子表的查询结果只能有一条(主表的数据视表关系而决定)
⑵反向查询:先查询主表,主表的查询结果也只能有一条,子表对应的数据可以有一条或多条,使用all()方法获取全部子表数据
4、基于双下划线的跨表查询的特点在于:子表和主表查询出来的结果都可以是多条(视具体的表关系而定)
5、使用"模型类.字段名"来获取某一个列具体的值时:
⑴前提是返回的是一个模型类,而不是一个QuerySet对象。如果是一个QuerySet对象,那么就需要通过for循环遍历来依次返回每个具体数据的模型类
⑵只有返回数据是一条数据的模型类时,才能使用"模型类.字段名"来获取某一个列具体的值:如一对多中,子表到主表,主表肯定只有一条数据,那么就可以直接使用"模型类.字段名"来获取某一个列具体的值
⑶如果返回的是多条数据的模型类,那么就必须先使用for循环来遍历依次得到每条数据的模型类,然后才能使用"模型类.字段名"来获取某一个列具体的值:如一对多中,主表到子表,子表数据可以有一条或多条,当为多条时先使用all()来获取全部数据(此时为一个QuerySet对象),然后遍历,再使用"模型类.字段名"
例3:
# -*- coding: utf-8 -*-
from django.http import HttpResponse
from polls.models import Author,Book,Publisher,BookOrder
from django.db import models
from django.db import connection
def index(request):
#查询"西游记"这本书的作者
#正向查询:按字段
result = Book.objects.filter(name="西游记").first()#如果不使用first()方法,则返回的是一个QuerySet对象,如果是一个QuerySet对象,那么就不能使用实例名.属性名
print(result.author_a)
print(type(result.author_a))#结果为一个模型类,不能使用.query方法
# 查询作者为"吴承恩"的图书
#反向查询:按表名小写_set.all()
result_1 = Author.objects.filter(name='吴承恩').first()#如果不使用first()方法,则返回的是一个QuerySet对象,如果是一个QuerySet对象,那么就不能使用小写子表模型名_set属性
book_1 = result_1.book_set.all()#子表中存在多条数据,使用all()方法获取全部数据
print(book_1)
return HttpResponse("success")
"""
Author object (1)
<class 'polls.models.Author'>
<QuerySet [<Book: Book object (1)>, <Book: Book object (3)>]>
"""
注:
一对一、一对多、多对多表关系的"基于对象的跨表查询"的查询方式,可以参照"表关系"那节