django—orm

注意:文档为逐步完善的结果,测试用表前后有不一致

1、orm表关系的建立

1.1 创表顺序

1、创建基表,即不需要创建外键的表

2、根据基表,创建带外键的表


1.2 创建外键的方式

从表之间的数据引用关系看,分为

一对一:指的是表和引用表之间的数据一一对应,不能重复

一对多:指得是表中的每一个记录都可以被其他表多次引用

多对多:两个表互为一对多的关系,即为多对多

代码:以图书表、作者表、出版社表为例

# 先创建基表:出版社表Publish
class Publish(models.Model):
    name = models.CharField(max_langth=64)
    addr = models.CharField(max_length=64)
# 创建作者表、作者详情表:关联作者详情表  
class AuthorInfo(models.Model):
    gender = models.CharField(max_length=64)
    age = models.IntegerField()
    phone = models.IntegerField()
class Author(models.Model):
    name = models.CharField(max_length=64)
    info = models.OneToOneField(to='AuthorInfo')  
    # 一对一关系:外键一般建立在查询频率较多的数据表
    
    

# 创建图书表
class Book(models.Model):
    name = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=6,decimal_places=2)
    publish = models.ForeignKey(to='Publish')  # to的参数也可以直接写表名,但是,该表必须在此之前定义
    # 多对一关系:外键建在多的表上
    
    auth = models.ManyToManyField(to='Author')  
    # 多对多关系
    '''django会自动创建一个表book_author,该表与book和author都是多对一的关系,即存储了book和author的多对多关系'''  

1.3 创建多对多表关系的三种方式

1.3.1 orm自动创建

代码:

class Authors(models.Model):
    pass
class Book(models.Model):
    authors = models.ManyToManyField(to='Authors')

优点:

1、orm自动创建第三张表

2、支持跨表查询

缺点:

1、关系表的生成由orm控制,包括表名,字段名,字段类型等等

2、无法直接操作表中的数据,只能通过orm的方法add()/set()/remove()/clear()简单操作

3、封装程度较高,操作耗时较多

1.3.2 手动创建

代码:

class Authors(models.Model):
    pass
class Book(models.Model):
    pass

class BookAutor(models.Model):
    book = models.ForeignKey(to='Book')
    authors = models.ForeignKey(to='Author')

优点:

1、关系表可以直接被操作,包括表结构、表数据

缺点:

1、数据表之间要通过两次跨表查询才能得到结果

# 由book查author
res1 = book_obj.bookAutor_set.authors.name

# 由author查book
res2 = author_obj.bookAutor_set.book.name

2、关系表不再支持orm的add()/set()/remove()/clear()

1.3.3 半自动创建

代码:

class Authors(models.Model):
    pass
class Book(models.Model):
    authors = models.ManyToManyField(to='Authors',through='BookAuthor',through_fields=('book','authors'))
    # through参数指定关联表
    # through_fields参数为一个元组,值为关系表的字段名,值的顺序为:(本表相关字段名,关联表相关字段名)
    # 两张数据表,只需一个表建立多对多外键即可

class BookAutor(models.Model):
    book = models.ForeignKey(to='Book')
    authors = models.ForeignKey(to='Author')

优点:

1、可以操作关系标,包括表结构、表数据

2、可以支持数据表orm跨表查询

缺点:

1、关系表不再支持orm的add()/set()/remove()/clear()


2、非外键字段数据

2.1 增加

1、create方法

# 以books表为例
# 在create方法中,以逗号分隔,以关键字参数传字段数据,它们是AND关系
book_obj = models.Books.objects.create()

2、对象的绑定方法save

'''
1、先实例化一个表数据对象
2、调用对象绑定方法save
'''
book_obj = models.Books()  # 实例化参数:关键字形式,字段名=字段值
book_obj.save()

2.2 查询

2.2.1 常用方法

(1)先获取QuerySet对象

QuerySet对象

filter查询出来的结果是一个QuerySet对象

  • 只要是QuerySet对象就可以无限制的调用QuerySet的方法

     res= models.Books.objects.filter(pk=1).filter().filter().filter().filter()
    
  • 只要是QuerySet对象就可以点query查看当前结果内部对应的sql语句

1、all()

查询所有,返回的结果为QuerySet对象

res = models.Books.objects.all()

2、filter()

筛选,返回结果为QuerySet对象

res = models.Books.objects.filter(pk=1,title='三')  # 支持多个参数 并且是and关系


3、get()

筛选,返回结果为数据对象

条件不存在直接报错 并且查询条件必须是唯一的

 res = models.Books.objects.get(title='西游记')

4、values()

获取数据对象中指定的字段的值

返回的结果为:queryset 对象,值为列表套字典,如:

res = models.Books.objects.values('title','price')
print(res)
'''
<QuerySet [{'title': '三国演义', 'price': Decimal('222.66')}, {'title': '红楼梦', 'price': Decimal('888.99')}, {'title': '西游记', 'price': Decimal('444.66')}, {'title': '西游记', 'price': Decimal('666.22')}]>
'''

5、values_list()

获取数据对象中指定的字段的值

返回的结果为:queryset 对象,值为列表套原则,如:

res = models.Books.objects.values_list('title','price')
print(res)
'''
<QuerySet [('三国演义', Decimal('222.66')), ('红楼梦', Decimal('888.99')), ('西游记', Decimal('444.66')), ('西游记', Decimal('666.22'))]>
'''

6、order_by()

按照指定的顺序排序

# 默认升序
res = models.Books.objects.order_by('price')
res1 = models.Books.objects.all().order_by('price')

# 降序:排序字段前加-号
res1 = models.Books.objects.all().order_by('-price')

7、reverse()

颠倒顺序

前提是跌倒的对象必须有顺序,即被排序过了的

res = models.Books.objects.all()
res1 = models.Books.objects.all().reverse()

8、exclude()

排除什么什么之外,返回的是QuerySet对象

res = models.Books.objects.all().exclude(title='三国演义')

9、distinct()

对查询结果进行去重操作

去重的前提:数据必须是完全相同的情况下才能够去重,包括主键

res = models.Books.objects.values('title','price')
res = models.Books.objects.values('title','price').distinct()

(2)基于QuerySet获取数据对象

4、first()

queryset中第一个数据对象

res = models.Books.objects.filter(title='西游记').first()

5、last()

queryset中第一个数据对象

res = models.Books.objects.filter(title='西游记').last()

(3)基于数据对象获取数据值

数据对象可以通过对象.属性的方式获取值

# 以Books表的一条数据对象为例
value = book_obj.name

(4)其他

1、exists()

判断查询结果是否有值,返回结果是一个布尔值

res = models.Books.objects.filter(pk=1).exists()

2、count()

统计数据的个数

num = models.Books.objects.count()

2.2.2 双下划线查询

语法:字段名__方法名

使用:一般作为filter()的过滤条件

下表中示例语句,省略models.Books.objects.

方法 说明 示例
__gt 大于 filter(price__gt=500
__lt 小于 filter(price__lt=400)
__gte 大于等于 filter(price__gte=500)
__lte 小于等于 filter(price__lte=500)
__in 在...中 filter(price__in=[222,444,500])
__range 在某区间内,参数为[],包含两端 filter(price__range=(200,800))
__year 是某年 filter(publish_date__year='2019')
__month 是某月 filter(publish_date__month='1')

2.2.3 模糊查询

语法:字段名__方法名

使用:一般作为filter()参数

以下示例,省略models.Books.objects.

方法 说明 示例
__startswith 以某字符串结尾 filter(title__startswith='三')
__endswith 以某字符串结束 filter(title__endswith='1')
__contains 包含某字符串 filter(title__contains='游')
__icontains 包含某字符串,但忽略大小写 filter(title__icontains='p')

2.3 修改

1、QuerySet方法

 models.Books.objects.filter(pk=1).update(price=444.66)

2、对象的绑定方法

# 先获取对象
book_obj = models.Books.objects.get(pk=1)

# 再调用对象的绑定方法
book_obj.save()

注意点:

利用对象的绑定方法save进行修改,内部其实是从头到尾将数据的所有字段都重新写一遍

而用update方法,是针对修改的字段进行的更新,操作效率高耗时少

2.4 删除

1、QuerySet方法

 models.Books.objects.filter(pk=1).delete()

2、对象的绑定方法

# 先获取对象
book_obj = models.Books.objects.get(pk=1)

# 再调用对象的绑定方法
book_obj.delete()

3、外键字段数据

3.0 级联更新

指的是:当一对一或多对一关系中,表中的外键字段指向的数据被删除/更新时,该表中的数据也会被删除/更新

mysql数据库,django1.x版本默认级联更新、级联删除,django2.x版本需要手动指定外键字段的约束条件

ON DELETE CASCADE 级联删除
ON UPDATE CASCADE 级联更新

3.1 一对一

在数据库中也是Foreign_key

3.2 一对多

3.2.1 增加

1、create()

同非外键字段的create方法,传主键值

2、传虚拟字段

# 先查询关联的数据对象
publish_obj = models.Publish.objects.filter(pk=2).first()

# 再将对象作为参数传给外键(定义中写到的外键,是一个虚拟字段)
models.Book.objects.create(title='红楼梦',price=444.33,publish=publish_obj)  # Book表中只有publish_id字段,publish字段是一个虚拟字段,是定义表时创建的

3.2.2 修改

1、update()

同非外键字段的update方法

models.Book.objects.filter(pk=1).update(publish_id=2)

2、传虚拟字段

使用update方法,但是以虚拟字段传参,同2.2.1

3.2.3 删除

models.Publish.objects.filter(pk=1).delete() 

2.2.4 注意点

django1.x默认级联删除,级联更新

3.3 多对多

以表Book和Author为例

1、先查有外键的表中的数据对象

book_obj = models.Book.objects.filter(pk=2).first()

2、数据对象点外键,可能直接获取外键关联的数据对象

book_obj.authors
# 获取到关联的数据对象,即现在已经从Book跨到Author了


# 反向查询:author_obj.book_set.all()

此时就能操作多对多的记录了

3.3.1 增加:add()

使用add方法,给多对多关系的第三张表,即关系表中添加对应的关系

绑定id值

book_obj.authors.add(1)  # 在第三张表里面给书籍绑定一个主键为1的作者

book_obj.author.add(1,2)  # 由于时多对多,因此可以绑定多个

传对象

# 先查出来对应的对象
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()

# 再使用add传值,操作关系表
book_obj.authors.add(author_obj,author_obj1)  # 可以绑定多个

3.3.2 修改:set()

使用set方法,修改多对多关系表中的数据

使用方式,同增加add

注意点:

传参数时,要用可迭代对象的形式,如元组

set((1,2))  # 传数字
set((author_obj,author_obj1))  # 传对象

3.3.3 删除:remove()

使用delete方法,删除多对多关系表中的数据

使用方式,同增加add

注意点:

传参数时,不需要使用迭代对象,跟add方法基本一致

3.3.4 清空 :clear()

删除某个数据在第三张表中的所有记录,不需要参数

book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.clear()

3.4 跨表查询

3.4.1 正反向查询

以A查B为例,A中定义了A和B的关系,有外键key

正向查询:从A查B

反向查询:从B查A

3.4.2 基于对象的跨表查询

# 正向查询 :对象.外键
a_obj = models.A.objects.filter(tk=2).first()
b_obj = a_obj.key # 对象.外键即可得到关联的对象

b_obj_list = a_obj.key.all()  # 当关联的数据对象有多个时,要加all()才能得到,否则得到的是app01.A.None

# 反向查询 :对象.关联的表名小写 + _set
b_obj = models.B.objects.filter(tk=3).first()
a_obj = b_obj.a_set

a_obj_list = b_obj.a_set.all()  # 同样,当关联多个数据时,要加all(),否则得到的是app01.B.None


# 注:因为django中默认的外键字段参数related_name=None,此时django的处理是反向查询的字段名就用目标表名小写_set

关键语法:

# 正向查询 :对象.外键

# 反向查询 :对象.关联的表名小写 + _set

# 当一对一关系时,反向查询不需要加_set

3.4.3 基于双下滑线的跨表查询

1、写外键字段就相当于已经跨到外键字段所关联的表

2、你想要改表的哪个字段信息 你只需要加__获取即可

3、models后面点的谁 就以谁为基表

关键语法:反向查询时,与对象的跨表查询稍有区别

# 正向查询 :外键__

# 反向查询 :表名小写__

示例:

1.查询书籍pk为2的出版社名称
    # 正向
res = models.Book.objects.filter(pk=2).values('publish__name') 
    # 反向
res = models.Publish.objects.filter(book__pk=2).values('name')


2.查询书籍pk为2的作者姓名和邮箱
res = models.Book.objects.filter(pk=2).values('authors__name','authors__email')
res = models.Author.objects.filter(book__pk=2).values('name','email')

3.查询作者是egon的家庭地址
res = models.Author.objects.filter(name='egon').values('author_detail__addr')
res = models.AuthorDetail.objects.filter(author__name='egon').values('addr')

4.查询出版社是东方出版社出版过的书的名字
res = models.Publish.objects.filter(name='东方出版社').values('book__title')
res = models.Book.objects.filter(publish__name='东方出版社').values('title')

5.查询书籍pk是2的作者的手机号
res = models.Book.objects.filter(pk=2).values('authors__author_detail__phone')
res = models.Author.objects.filter(book__pk=2).values('author_detail__phone')


4、聚合查询

4.1 聚合函数

Max/Min/Sum/Avg/Count

需要导入模块:from django.db.models import Max, Min, Sum, Count, Avg

关键语法:aggregate(聚合结果别名=聚合函数(参数))

查询结果:使用聚合函数,从每一个组中获取结果:字典

注意点:

1、聚合函数必须在分组之后才能使用

2、没有分组,即默认整体就是一组

3、查询结果为普通字典

{'key': Decimal('99.00')}  # value不是字符串,显示的是标明了字段类型的数据,通过字典取值之后,得到的是实际的值
# 参数一般为字段名,如:
models.Book.objects.aggregate(mr=Max('price'))
'''
{'mr': Decimal('99.00')}
'''

models.Book.objects.aggregate(Max('price'))
'''
{'price__max': Decimal('99.00')}
'''

示例:

# test.py 在测试文件中测试,打印结果。以下几组查询都没有进行分组,即默认整体是一组

# 1.筛选出价格最高的书籍
res = models.Book.objects.aggregate(mr = Max('price'))

# 2.求书籍总价格
res = models.Book.objects.aggregate(sm = Sum('price'))

# 3.求书籍平均价格
res = models.Book.objects.aggregate(av = Avg('price'))

# 4.同时使用多个聚合函数
res = models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Count('price'),Avg('price'))

4.2 分组查询

1、关键字:annotate

2、默认以当前表id分组,并使用聚合函数在组内进行聚合计算,得到结果作为查询结果中组内的属性

# 按书分组,并查询每本书的作者人数
# 在查询结果集中,会给每个书对象添加一个author_num属性
res = models.Book.objects.annotate(author_num=Count('authors__id'))

# SELECT `drm_test_book`.`id`, `drm_test_book`.`name`, `drm_test_book`.`price`, `drm_test_book`.`publishs_id`, COUNT(`drm_test_bookauthor`.`authors_id`) AS `author_num` FROM `drm_test_book` LEFT OUTER JOIN `drm_test_bookauthor` ON (`drm_test_book`.`id` = `drm_test_bookauthor`.`books_id`) GROUP BY `drm_test_book`.`id` ORDER BY NULL

可以使用多个聚合函数,并且使用values控制要展示的属性

# 求出每本书的作者人数和每本书的作者中最大的年龄
res = models.Book.objects.annotate(author_num=Count('author__id'), max_age=Max('author__age'))

3、分组之后的结果为queryset对象,包含的元素是一个个的组。基于此进行的操作是对组的筛选

# 求出东方出版社出版的书的作者人数和作者最大年龄
res = models.Book.objects.annotate(author_num=Count('author__id'), max_age=Max('author__age')).filter(publish__name='东方')

'''
<QuerySet [<Book: Book object>]>
'''

4、需要注意的是:使用values控制查询结果的展示,得到的queryset对象中的元素是包含了要展示的数据的普通字典,而不是模型对象

res = models.Book.objects.annotate(author_num=Count('author__id'), max_age=Max('author__age')).filter(
        publishs__name='东方').values('name', 'author_num', 'max_age')

'''
<QuerySet [{'name': '小明自传', 'author_num': 0, 'max_age': None}]>
'''

示例:

1.统计每一本书的作者个数、书名和对应的作者人数
res = models.Book.objects.annotate(author_num=Count('authors__id')).values('title','author_num')
print(res)

2.统计出每个出版社卖的最便宜的书的价格、出版社的名字和价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
print(res)

按照其他字段分组
res = models.Publish.objects.values('想要分组的字段名').annotate(min_price=Min('book__price')).values('name','min_price')
print(res)


3.统计不止一个作者的图书
res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title','author_num')
print(res)

4.查询各个作者出的书的总价格  作者名字  总价格
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
print(res)

5、F查询

5.1 使用F查询

F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值

需要导入模块:from django.db.models import F

示例:

from django.db.models import F
# 1. 查询库存数大于卖出数的书籍
res = models.Book.objects.filter(kun_cun__gt = F('mai_cun')).values('title')  # 后面的条件是来自于数据库的其他字段值


# 2. 将所有书的价格上涨100块
models.Book.objects.all().update(price=F('price') + 100)


5.2 利用F查询,修改char字段

# 将所有书的名称后面全部加上 "爆款" 后缀
# 操作字符串数据需要借助于Concat方法
from django.db.models.functions import Concat
from django.db.models import Value
ret3 = models.Book.objects.update(title=Concat(F('title'), Value('爆款')))

6、Q查询

6.1 Q查询的基本使用方法

filter() 等方法中逗号隔开的条件是and的关系

如果你需要执行更复杂的查询(例如OR语句),你可以使用Q对象

操作符:

&:代表AND

|:代表OR

~:代表取反

注意点:

0、使用逗号分隔多个参数

1、可以组合使用操作符以及括号来编写任意复杂的Q对象

2、查询函数可以混合使用Q 对象和关键字参数

3、所有提供给查询函数的参数(关键字参数或Q 对象)都将AND在一起

4、但是,如果出现Q 对象,它必须位于所有关键字参数的前面

示例:

from django.db.models import Q

# 1.查询一下书籍名称是三国演义 或者 库存数是500的书籍
res = models.Book.objects.filter(title='三国演义',kun_cun=500)  # and关系
res = models.Book.objects.filter(Q(title='三国演义'),Q(kun_cun=500))  # Q包裹之后逗号还是and关系
res = models.Book.objects.filter(Q(title='三国演义') | Q(kun_cun=500))  #  |就是or的关系
res = models.Book.objects.filter(~Q(title='三国演义') | Q(kun_cun=500))  #   ~就是not关系
print(res)

6.2 Q查询的高级使用方法

# 提前生成Q对象,并使用append方法添加条件
q = Q()
q.connector = 'or'  # 默认是and  可以改成or
q.children.append(('title','三国演义'))
q.children.append(('kun_cun__gt',500))
res = models.Book.objects.filter(~q)  # 取反
print(res)

7、orm字段及参数

7.1 常用字段

CharField     varchar
IntegerField   int
BigIntegerField   bigint
EmailField    varchar(254)
DateField
DateTimeField
'''
	参数说明:
	auto_now:每次修改数据的时候 都会自动将当前修改时间更新上去  实时更新
    auto_now_add:在创建数据的时候 会将当前时间自动记录 之后不会自动修改  除非你人为修改
	'''
AutoField     auto_increment   

BooleanField    布尔值  
'''
	该字段在存储的时候 你只需要传布尔值True或False
	它会自动存成1/0
'''

TextField  专门用来存大段文本

FileField  专门用来文件路径   '/etc/data/a.txt'   

upload_to = '/etc/data'
'''
	给该字段传值的时候 直接传文件对象
	会自动将文件对象保存到upload_to后面指定的文件路径中
	然后将路径保存到数据库
'''

DecimalField(Field)
'''
	1、10进制小数
	2、参数:
    max_digits,小数总长度
    decimal_places,小数位长度
'''

7.2 自定义char字段

class FixCharField(models.Field):
    '''
    自定义的char类型的字段类
    '''
    def __init__(self,max_length,*args,**kwargs):
        self.max_length=max_length
        super().__init__(max_length=max_length,*args,**kwargs)

    def db_type(self, connection):
        '''
        限定生成的数据库表字段类型char,长度为max_length指定的值
        :param connection:
        :return:
        '''
        return 'char(%s)'%self.max_length
#应用上面自定义的char类型
class Class(models.Model):
    id=models.AutoField(primary_key=True)
    title=models.CharField(max_length=32)
    class_name=FixCharField(max_length=16)
    gender_choice=((1,'男'),(2,'女'),(3,'保密'))
    gender=models.SmallIntegerField(choices=gender_choice,default=3)

7.3 特殊参数choices

1、定义字段时,指定参数choices

2、choices的值为元组套元组(事先定义好,一般在该表类中定义该字段之前定义)

3、每一个元组存放两个元素,形成一种对应关系

4、往数据库中存值时,该字段的值存为元组第一个元素即可

5、通过对象.get_字段名_display()获取对应的元组第二个元素值,当没有对应关系时,返回的是它本身

示例:

# 定义一个User表
class User(models.Model):
    username = models.CharField(max_length=64)
    gender_choices = ((1,'男'),(2,'女'),(3,'其他'))
    gender = models.IntegerField(choices=gender_choices)
    
# 同步迁移到数据库
python manage.py makemigrations
python manage.py migrate

# 在test.py配置测试环境
# 录入数据
models.User.objects.create(username='aaa', gender=1)
models.User.objects.create(username='bbb', gender=2)
models.User.objects.create(username='ccc', gender=3)
models.User.objects.create(username='ddd', gender=4)

# 验证choices
for user_obj in models.User.objects.all():
    print(user_obj.gender,end=' ')
    print(user_obj.get_gender_display())
'''
1 男
2 女
3 其他
4 4
'''    

8、orm查询优化

即尽量减少对数据库的操作,减轻数据库的压力

8.1 orm惰性查询

orm内所有的sql查询语句,都是惰性操作,即当你需要使用查询结果时,才会真正去执行sql语句访问数据库,否则是不会执行查询的

可使用代码验证:

1、配置settings文件,使得当执行sql语句时,在终端打印sql命令

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

2、验证

# 只查询,不使用查询结果
models.User.objects.all()
# 将查询结果赋值给变量res
res = models.User.object.all()

# 分别执行,查看终端的打印结果

8.2 only与defer

only查询:指定查询某字段时,将全部结果一次性获取出来,以后再访问该字段数据时,直接从内存中拿即可,不需要再操作数据库

defer查询:指的是一次性获取指定字段之外的所有字段数据,封装到对象中,以后再访问这些字段数据时,直接从内存中拿即可,不需要再操作数据库

代码:

# 同样,可以配置settings,在终端中打印被执行的sql命令

# all()查询
res = models.User.objects.all()  # 访问一次数据库,打印一条sql命令,获取所有的User表中的数据到内存

# only查询
res = models.User.objects.only(username)  # 访问一次数据库,打印一条sql命令,仅仅获取username的所有数据到内存
for user_obj in res:
    print(user_obj.username)  # 直接从内存中获取,不访问数据库,不打印sql命令
    print(user_obj.gender)  # 每执行一次,就访问一次数据库,打印一次sql命令
    
# defer 查询
res = models.User.objects.defer(usernmae)  # 访问一次数据库,打印一条sql命令,获取除了username之外的所有数据到内存
for user_obj in res:
    print(user_obj.username)  # 每执行一次,访问一次数据库,打印一条sql命令
    print(user_obj.gender)  # 直接从内存中获取,不访问数据库,不打印sql命令

都支持正反向跨表查询,规则等同于基于对象的正反向查询,正向:'外键',反向:'表名小写_set'

select_related:指定外键,连表查询(inner join),获取所有数据到内存,不能指定多对多关系的外键

res = models.Book.objects.all().select_related('publishs')

'''
SELECT `drm_test_book`.`id`, `drm_test_book`.`name`, `drm_test_book`.`price`, `drm_test_book`.`publishs_id`, `drm_test_publish`.`id`, `drm_test_publish`.`name` FROM `drm_test_book` INNER JOIN `drm_test_publish` ON (`drm_test_book`.`publishs_id` = `drm_test_publish`.`id`) LIMIT 21;
'''

prefetch_related:指定外键,先查询本表所有数据,再根据外键查询对应的数据表所有数据,相当于子查询,可以指定多个外键,即多个子查询

res = models.Book.objects.all().prefetch_related('publishs')

'''
SELECT `drm_test_book`.`id`, `drm_test_book`.`name`, `drm_test_book`.`price`, `drm_test_book`.`publishs_id` FROM `drm_test_book` LIMIT 21;

SELECT `drm_test_publish`.`id`, `drm_test_publish`.`name` FROM `drm_test_publish` WHERE `drm_test_publish`.`id` IN (2);
'''
posted @ 2019-11-27 23:47  W文敏W  阅读(826)  评论(0编辑  收藏  举报