Django开发博客系统(05-QuerySet的使用)
Posted ylnx-tl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django开发博客系统(05-QuerySet的使用)相关的知识,希望对你有一定的参考价值。
在Model层中,Django通过objects属性来提供数据操作的接口,
比如我们想要查询所有文章的数据,只需要
Post.objects.all()
他会返回一个QuerySet对象,当我们需要用到它时,它会去db中获取数据,注意是用到的时候才会去查,而不是定义的时候去查,原因是QuerySet要支持链式操作,如果每次执行都要查询数据库的话,会影响性能,假设我要查询所有文章中状态为正常的文章,有如下代码.
posts = Post.objects.all() available_posts = posts.filter(status=1) print(available_posts)
如果每次都要查询数据库的话,那么我们的posts中的所有文章的数据,其实根本就没有用到,而如果是懒查询则没有这个问题,这里贴上官方文档中的解释
链式调用
链式调用就是,执行一个对象中的方法之后得到的结果还是这个对象,比如
posts = Post.objects.all().filter(status=1).filter(category_id=2)...
可以这样一直调用下去(当然也可以filter(status=1, category_id=2),但这两种写法在处理多对多时会有区别,因为我自己暂时搞得不是很懂,所以这里不多叙述以免误人,详情还请查阅官方文档)
常用的QuerySet接口
根据是否支持链式调用来分类.
支持链式调用的接口
显而易见,该类接口返回的都是QuerySet对象.
l all相当于SELECT * FROM table_name,用于查询所有数据
l filter根据条件过滤数据,常用的条件比如等于,不等于,大于,小于.当然也能改成
LIKE查询:
Model.objects.filter(content__contains=‘条件‘)
l exclude类似filter,逻辑相反
l reverse把QuerySet倒序排列
l distinct去重
l none返回空的QuerySet
不支持链式调用的接口
返回值不是QuerySet.
l get 如果存在则返回对应的实例,不存在则会抛出DoesNotExist异常,在使用时请配合try使用.
try: post = Post.objects.get(id=1) except Post.DoesNotExist: pass
l create 用来创建一个Model对象.
post = Post.objects.create(title="5-QuerySet的使用")
l get_or_create 根据条件查找,没有则创建.
l update 根据条件批量更新记录.
Post.objects.filter(owner__name=‘友利奈绪‘).update(title=‘测试‘)
l update_or_create 同get_or_create,用于更新.
l count 用于返回QuerySet中有多少条记录
l latest 返回最新的一条字段,使用时需要在Model中定义
class Meta: get_latest_by = <用于排序的字段>
l earliest 同上,返回最早的一条记录
l first 返回第一条记录
l last 返回最后一条记录
l exists 返回True 或 False
l bulk_create 用来批量保存记录,参数是一个由对应Model对象组成的列表或者元组
l in_bulk 批量查询,返回值为字典,参数为id_list和filed_name
Post.objects.in_bulk([1, 2, 3]) # 查询id为1,2,3的数据 {1: <Post>, 2: <Post>, 3: <Post>}
l delete 根据条件批量删除记录
l values 当我们只需要某个字段的值,不需要Model实例时,使用这个
title_list = Post.objects.filter(category_id=1).values(‘title‘)
返回的结果是包含dict的QuerySet, <QuerySet [{‘title‘: xxx}]>
l values_list 类似于values, 返回的是包含tuple的QuerySet, 如果只是一个字段的话请加上flat=True
进阶接口
用于提升性能的接口,在优化项目时要考虑使用
l defer 把不需要展示的字段延迟加载,比如我们要获取Post中除了content的其他字段.
posts = Post.objects.all().defer(‘content‘) for post in posts: # 此时会执行数据库查询,但不会加载content print(post.content) # 此时会执行数据查询,获取到content
书中提到这段代码会产生N+1的查询问题,他人的说明
l only 与defer接口相反, 如果只想获取content,那么使用only,其他值获取时会产生额外的查询.
l select_related 用来解决外键产生的N+1问题,用代码来说明.
posts = Post.objects.all() for post in posts: # 产生数据库查询 print(post.owner) # 产生额外的数据库查询
owner是外键.
解决方法
posts = Post.objects.all().select_related(‘owner‘) for post in posts: # 在这里会把owner一起查出来 print(post.owner)
l prefetch_related 针对多对多的数据,用它来避免N+1.比如我们项目中的post和tag
posts = Post.objects.all().prefetch_related(‘tag‘) for post in posts: # 产生两条查询语句,分别查询post和tag print(post.tag.all())
常用的字段查询
这里只罗列了一些常用的关键字,更多的请参考文档
l contains包含,用来进行相似查询
l icontains同上,但忽略大小写
l exact精确匹配
l iexact同上,忽略大小写
l in指定某个集合,比如
Post.objects.filter(id__in=[1, 2, 3])
l gt大于某个值
l gte大于等于
l lt小于
l lte小于等于
l startswith 以某个字符串开头,类似 LIKE ‘<关键词>%‘
l istartswith 同上,忽略大小写
l endswith以某个字符串结尾
l iendswith 同上,忽略大小写
l range范围查询,多用于时间范围
Post.objects.filter(created_time__range=(‘2018-05-01‘, ‘2018-06-01‘))
进阶查询
l F 常用于执行数据库层面的计算,避免出现多线程时的竞争状态
post = Post.objects.get(id=1) post.pv = F(‘pv‘) + 1 post.save()
l Q 用来解决OR查询,当然AND查询也可以
post = Post.objects.filter(Q(id=1) | Q(id=2)) post = Post.objects.filter(Q(id=1) & Q(title=‘‘))
l Count 用来聚合查询,比如我们想要知道某个分类下有多少文章,简单的做法
category = Category.objects.get(id=1)
posts_count = category.post_set.count()
但假如我们想把这个结果放到category上,则.
categories = Category.objects.annotate(posts_count=Count(‘post‘))
这相当于给category动态增加了属性posts_count
l Sum 类似于Count, 用来做合计,比如我们想要得到目前所有文章的累计访问量
Post.objects.aggregate(all_pv=Sum(‘pv‘)) # 输出结果类似: {‘all_pv‘: 488}
同时上面用到了annotate和aggregate,前者用来给QuerySet结果增加属性,后者用来直接计算结果.出了Count和Sum外,还有Avg,Min,Max等..
最后,Django还提供了SQL的接口
Post.objects.raw(‘SELECT * FROM blogApp_post‘)
可以解决QuerySet无法满足查询的情况,还能提高执行效率,不过请谨慎使用,以免提高维护成本.
详细资料请参考官方文档
以上是关于Django开发博客系统(05-QuerySet的使用)的主要内容,如果未能解决你的问题,请参考以下文章