Django知识碎片整理(三)

Django查询技巧详解:从外键到多对多
本文详细介绍了Django中的JsonResponse使用,外键的正向与反向查询,多对多关系操作,聚合与分组查询,以及F查询和Q查询的应用。内容涵盖设置JsonResponse返回JSON数据,外键的双向查找,多对多表的查询、创建、更新和删除,以及聚合函数与分组查询在实际场景中的应用。

碎片一:Django的JsonResponse

我们在写django时在views需要定义返回json格式的数据给前端的视图函数,这个时候可以用上JsonResponse,它是Django自带的处理json格式数据的资源

1、配置urls

2、编写视图函数

 3、访问响应地址,就能看到返回的数据是json数据(JsonResponse将我们的datadict数据自定转成了json格式)

注意:下面介绍如何使用JsonResponse返回列表数据,只需要在JsonResponse的参数将safe设置为false即可,不检查是否是字典格式

碎片二:外键的正向查询与反向查询 

1、正向查找与反向查找的含义

#出版社
class Publisher(models.Model):
    '''出版社数据表'''
    id=models.AutoField(primary_key=True)   #自增ID主键
    name=models.CharField(max_length=50,verbose_name='出版社名称',null=False,unique=True)
    addr=models.CharField(max_length=128,verbose_name='出版社地址',default='成都市动物园')

    def __str__(self):
        return '{},{}'.format(self.id,self.name,self.addr)

#书
class Book(models.Model):
    '''书数据表'''
    id=models.AutoField(primary_key=True)
    title=models.CharField(max_length=50,verbose_name='书名',null=False,unique=True)
    publisher=models.ForeignKey(to="Publisher")

    def __str__(self):
        return '{},{},{}'.format(self.id,self.title,self.publisher)

如上图可以看到,在Book字段中设置了外键publisher关联到表Publisher,如果从Book到Publisher进行查找,那么就是正向查找,如果是Publisher到Book进行查找,那么就是反向查找

2、正向查询与反向查询的具体代码操作

基于对象的正向查询:通过book表外键获取到publisher对象,然后去查询到Publisher表中对应对象的相关字段值

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *

    #外键查询操作-正向查询
    book_obj=Book.objects.all().first()
    #获取到外键关联的对象
    get_pub=book_obj.publisher
    #获取外键对象的属性
    print(get_pub.id)   #id
    print(get_pub.name) #name
    print(get_pub.addr) #addr

 基于双下划线的正向查询:跨表查询(查询出来是一个对象信息,双下划线就表示跨了一张表),book表中有一个publisher外键字段,通过双下划线表示跨到外键对应表中查询name字段

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *

    #外键查询操作-跨表查询
    #查询id是1的出版社的名称
    get_pubname=Book.objects.filter(id=1).values('publisher__name')
    print(get_pubname)

基于对象的反向查询

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *

    #反向操作
    #查询出第一个出版社
    get_publisher=Publisher.objects.all().first()
    #反向查询对应的书籍有哪些,反向查询的表_set就是反向查询
    get_books=get_publisher.book_set.all()
    print(get_books)

注意:

1、反向查询首先是获取的一个外键指定表publisher的对象get_publisher,然后通过book表进行反向查询对应的书籍,就是利用book_set进行反向查询

2、如果在book表中设置了related_name,那么book_set需要写成related_name的名称

利用双下划线的反向查询

利用双下划线进行反向查询的话,那么我们需要在外键的表中设定related_name

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *

    # 反向操作-双下划线,必须要先设置related_name
    get_book_title=Publisher.objects.filter(id=1).values('books__title')
    print(get_book_title)

注意:外键补充说明,在使用orm时需要确定是具体的对象还是queryset,针对不同的情况使用不同的函数

碎片三:多对多查询

多对多表在django当中是以manytomany进行关系确立的,下面介绍多对多表相关操作

1、查询:查询某一个作者所编写的书籍

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *

    #多对多
    #1、查询id为1的作者写的书籍
    author_obj=Author.objects.all().first()    #获取到某一作者对象
    get_book=author_obj.book.all()    #获取到该作者对象对应的所有书籍
    print(get_book)

2、创建:为某一个作者新增编写的书籍,会自动保存到多对多关系表中

会做两件事:第一件事是在book表中创建一个新增的书籍,第二件事是在作者与书的关系表中添加关联记录

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *

    #创建:通过作者创建一本书
    author_obj=Author.objects.all().first()     #获取一个作者对象
    author_obj.book.create(title='水浒传',publisher_id=1)  #添加作者对象对应的书籍

 3、添加:将现有一本书籍添加到作者当中,使得作者与该书籍有关联关系

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *

    #多对多
    #添加关系:某本书添加作者
    book_obj=Book.objects.get(id=5)     #获取一个书籍对象
    author_obj=Author.objects.all().first() #获取一个作者对象
    author_obj.book.add(book_obj)   #作者与书籍新增关联关系

 也可以直接添加书籍id进行添加

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *

    #直接添加id添加关联关系
    author_obj=Author.objects.all().first() #获取作者对象
    author_obj.book.add(1)  #直接添加书籍id

4、添加多个: 将现有多本书籍添加到作者当中,使得作者与该书籍有关联关系

注意:获取到多本书的对象集(列表形式),在add时需要将列表打散,使用*

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *

    #添加关系:多本书添加作者关联关系
    book_objs=Book.objects.filter(id__gt=2)     #获取对象集(列表)
    author_obj=Author.objects.all().first() #获取一个作者对象
    author_obj.book.add(*book_objs)   #作者与书籍新增关联关系,需要拆分

5、更新关联关系:更新某个作者对应书籍的关联关系

注意:set函数中的值一定要写成列表形式,更新为单个对应关系就在列表写一个值,例如[xxx],如果需要更新多个对应关系就在列表中写多个值,例如[xxxx,xxxx,xxxx],下面是更新了一条关联关系

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *

    #多对多
    #更新model对象的关联对象
    author_obj=Author.objects.get(id=3)     #获取作者对象
    author_obj.book.set([4])      #更新关联对象

6、删除:删除某本书与作者的关联关系

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *

    #多对多
    #删除某一个与作者的关联关系的书籍
    book_obj=Book.objects.get(id=1)     #获取书籍对象
    author_obj=Author.objects.all().first()     #获取作者对象
    author_obj.book.remove(book_obj)    #删除关联关系

 如上图可见,id为1的书籍和作者id为1的作者没有关联关系了,当然我们也可以直接通过书籍id直接删除

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *

    
    #通过书籍id直接删除关联关系
    author_obj=Author.objects.all().first() #获取作者对象
    author_obj.book.remove(2)     #通过书籍id直接删除关联关系

 如上图可见,id为2的书籍和作者id为1的作者没有关联关系了

 7、清空:将某个作者对应的所有书籍关联关系全部清空

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *

    #清空:将某个作者对应的书籍关联关系全部清空
    author_obj=Author.objects.get(id=3) #获取作者对象
    author_obj.book.clear()     #清空作者对象的所有关联关系

 如上图可见,作者id为3的已经没有书籍与之对应关联了

碎片四:聚合查询与分组查询

一、聚合查询

1、求平均值:求所有书籍的平均价格

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *
    from django.db.models import Avg

    #聚合查询
    avg_price=Book.objects.all().aggregate(Avg('price'))    #求所有书籍的平均价格
    print(avg_price)

 2、求和:求所有书籍价格的总和

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *
    from django.db.models import Sum

    #聚合查询
    sum_price=Book.objects.all().aggregate(Sum('price'))    #求所有书籍的总价格
    print(sum_price)

3、求最大值:求所有书籍价格最大值

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *
    from django.db.models import Max

    #聚合查询
    max_price=Book.objects.all().aggregate(Max('price'))    #求所有书籍的最大价格
    print(max_price)

4、求最小值:求所有书籍价格最小值

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *
    from django.db.models import Min

    #聚合查询
    min_price=Book.objects.all().aggregate(Min('price'))    #求所有书籍的最小价格
    print(min_price)

 注意:我们可以手动指定key的值,这样得到的字典就是我们定义的key和获取到的值value

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *
    from django.db.models import Min

    #聚合查询
    min_price=Book.objects.all().aggregate(get_minprice=Min('price'))   #求所有书籍的最小价格
    print(min_price)
    print(min_price['get_minprice'])    #获取到具体的值

5、多个聚合查询:计算出多个聚合的结果

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *
    from django.db.models import Min,Max,Sum,Avg

    #聚合查询
    res=Book.objects.all().aggregate(min_price=Min('price'),max_price=Max('price'),
                                     sum_price=Sum('price'),avg_price=Avg('price'))
    print('最小价格:{}'.format(res['min_price']))
    print('最大价格:{}'.format(res['max_price']))
    print('价格总和:{}'.format(res['sum_price']))
    print('平均价格:{}'.format(res['avg_price']))

二、分组查询

示例1:查询每一本书对应的作者的数量

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *
    from django.db.models import Min,Max,Sum,Avg,Count

    #分组查询
    #查询每一本书的作者个数
    #获取对象
    get_objs=Book.objects.all().annotate(author_num=Count('author'))
    for obj in get_objs:
        print('书名:{},数量:{}'.format(obj.title,obj.author_num))

上面的代码解析:

1、通过Book表查询出所有的书籍

2、通过author_num即根据作者个数进行分组查询,就是group_by

3、将对象集反馈给get_objs,每个对象会多一个author_num属性

4、循环遍历出每个对象(每本书)对应的作者的数量(author_num)并打印出来

示例2:查询作者数量大于1的书籍

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *
    from django.db.models import Min,Max,Sum,Avg,Count

    #分组查询
    #查询作者数量大于1的书
    get_books=Book.objects.all().annotate(author_num=Count('author')).filter(author_num__gt=1)
    for book in get_books:
        print('书名:{},作者数量:{}'.format(book.title,book.author_num))

 上面的代码解析:

1、通过Book表查询出所有的书籍

2、通过author_num即根据作者个数进行分组查询,就是group_by

3、分组后再通过作者数量进行查询,查询出作者数量大于1的书籍对象

4、将对象集反馈给get_books,每个对象会多一个author_num属性

5、循环遍历出每个对象(每本书)对应的作者的数量(author_num)并打印出来

示例3:求每个作者的书籍的总价格

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *
    from django.db.models import Min,Max,Sum,Avg,Count

    #分组查询
    #查询各个作者出的书的总价格
    get_authors=Author.objects.all().annotate(sum_price=Sum('book__price'))
    for author in get_authors:
        print('作者:{},书籍总价格:{}'.format(author.name,author.sum_price))

 上面的代码解析:

1、首先在作者表Author表获取到全部的作者

2、通过书籍总价格进行分组,即group_by,此时我们需要得到作者对应的书籍并计算总价格,所以此时我们需要设置sum_price

3、利用聚合函数Sum,然后对book进行跨表查询出每个作者对应的每本书的价格,利用Sum求和后给sum_price

4、最后将获取到的分组查询对象集给get_authors,每个对象会多了一个sum_price属性

5、遍历get_authors,将每个作者author的名称与书籍总价格打印出来

碎片五:F查询与Q查询 

一、F查询-用于同一张表两个字段比较

我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?

Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

示例1:查询库存数大于销售数的书籍

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *
    from django.db.models import F,Q

    #F&Q查询
    #1、查询出库存数大于卖出数的所有书籍(两个字段做比较)
    get_books=Book.objects.filter(kucun__gt=F('maichu'))
    print(get_books)

上面的代码就是利用filter筛选出每种书籍库存数大于卖出数的对象,利用F(‘maichu’)去针对每个书籍对象去数据库获取出对应的卖出数量进行比较

Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。

models.Book.objects.filter(commnet_num__lt=F('keep_num')*2)

 修改操作也可以使用F函数,比如将每一本书的价格提高30元

models.Book.objects.all().update(price=F("price")+30)

引申:

如果要修改char字段咋办?

示例1:把所有书名后面加上(第一版)

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *
    from django.db.models import F,Q
    from django.db.models.functions import Concat
    from django.db.models import Value

    #F&Q查询
    #3、针对字符串进行F操作,在每本书的书名加上(第一版)
    Book.objects.all().update(title=Concat(F('title'),Value('第一版')))

 修改char字段时,我们需要导入Concat和Value,Concat用于字符串拼接,Value用于包裹字符串

二、Q查询

filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR语句),你可以使用Q对象。

示例1:查询卖出数大于100或者价格小于100的书籍

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *
    from django.db.models import F,Q

    #F&Q查询
    #4、Q查询,卖出数大于100或者价格小于100的书籍
    get_obj=Book.objects.filter(Q(maichu__gt=100) | Q(price__lt=100))
    print(get_obj)
<QuerySet [<Book: 2,《Python基础》第一版,2,北京大学出版社,成都市动物园>, <Book: 3,《Python Locust》第一版,3,西华大学出版社,成都市动物园>, <Book: 4,《自动化测试框架实践》第一版,1,清华大学出版社,成都市动物园>, <Book: 5,《测试开发实践》第一版,1,清华大学出版社,成都市动物园>, <Book: 8,《战狼7》第一版,4,蓝光coco国际出版社,蓝光coco国际二期>, <Book: 9,《流浪地球》第一版,4,蓝光coco国际出版社,蓝光coco国际二期>, <Book: 10,水浒传第一版,1,清华大学出版社,成都市动物园>]>

示例2:查询卖出数大于100并且价格小于100的书籍

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *
    from django.db.models import F,Q

    #F&Q查询

    #Q查询,卖出数大于100并且价格小于100的书籍
    get_obj=Book.objects.filter(Q(maichu__gt=100) & Q(price__lt=100))
    print(get_obj)
<QuerySet [<Book: 3,《Python Locust》第一版,3,西华大学出版社,成都市动物园>, <Book: 8,《战狼7》第一版,4,蓝光coco国际出版社,蓝光coco国际二期>]>

示例3:查询卖出数大于100并且价格小于100并且书名包含Locust的书籍

import os

if __name__=='__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE','booksys.settings')
    import django
    django.setup()

    from bookapp.models import *
    from django.db.models import F,Q

    #F&Q查询
    #Q查询结合自定义查询,查询卖出数大于100并且价格小于100并且书名包含Locust的书籍
    get_book=Book.objects.filter(Q(maichu__gt=100) & Q(price__lt=100),title__contains='Locust')
    print(get_book)
<QuerySet [<Book: 3,《Python Locust》第一版,3,西华大学出版社,成都市动物园>]>

注意:自定义查询条件只能写到Q查询的后面,不能写到Q查询的前面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值