Django QuerySet

基本概念

在Django的Model层,Django通过给Model增加一个objects属性来提供数据操作的接口.
例如,我们想获取所有学生的所有信息,可以写成Student.objects.all(),这样我们就能拿到QuerySet对象.这个对象包含了我们需要的数据,当我们用到时,它才会去数据库中获取数据。
也就是说,QuerySet本质上是懒加载的对象,这也是因为QuerySet要支持链式调用。

链式调用:执行一个对象中的方法后得到的结果还是这个对象,这样可以接着执行对象上的其他方法。

支持链式调用的接口

支持链式调用的接口即返回QuerySet的接口:

  • all : 用于查询所有数据
  • filter : 条件过滤
  • exclude :类似filter,只不过是相反的逻辑,不满足条件的
  • reverse : 把QuerySet中的结果倒序排列
  • distinct : 去重
  • none : 返回空的QuerySet

不支持链式调用的接口

  • get : 比如Student.objects.get(id=1),用于查询id为1的学生信息,如果存在,则直接返回对应的Student实例;如果不存在,则抛出DoesNotExist异常,所以一般会做异常处理
    1
    2
    3
    4
    try:
    student = Student.objects.get(id=1)
    except Student.DoesNotExist:
    pass
  • create : 直接创建一个model对象。比如student = Student.objects.create(name="小明")
  • get_or_create : 根据条件查找,如果没有找到就创建
  • update_or_create :更新或新建
  • count : 用于返回QuerySet有多少条记录
  • latest : 返回最新的一条记录,但是需要在model中的Meta中定义:get_lastest_by=用来排序的字段
  • earliest : 返回最早的一条记录
  • first : 从当前QuerySet记录中获取第一条
  • last : 同上,返回最后一条
  • exists : 返回True或者False,判断符合条件的数据是否存在
  • bulk_create : 批量创建记录
  • in_bulk : 批量查询,接受两个参数id_listfiled_name。可以通过Student.objects.in_bulk([1,2,3])查询出id为1,2,3的数据,返回结果是字典类型,字典的key为查询条件。返回结果示例{1:<实例1>,2:<实例2>,3:<实例3>,}
  • update : 根据条件批量更新记录
  • delete : 根据条件批量删除。

    注意!update和delete都会触发Django的signal

  • values : 只返回某个字段的值。返回的结果是包含dict的QuerySet
  • values_list : 返回包含元组的QuerySet,如果只是一个字段的话,可以通过flat=True,变为一维数组

不常用但可能会很有效的接口

  • defer : 把不需要展示的字段做延迟加载。例如不想加载某个过大的字段,就可以使用defer
  • only : 只加载指定字段
  • select_related : 通常用来解决外键产生的N+1的问题。只能用来解决一对多的关联关系

    N+1问题:一条查询请求返回N条数据,当我们操作数据时,又会产生额外的请求,这就是N+1问题,所有的ORM框架都存在这样的问题。通常由外键查询产生。

    1
    2
    3
    4
    # 假设学生表和老师表通过外键关联,一对多关系
    students = Student.objects.all()
    for s in students: # 产生数据库查询
    print(s.teacher_name) # 需要再次查询

    这种问题的解决方法就是使用select_related

    1
    2
    3
    students = Student.objects.all().select_related("teacher_name")
    for s in students: # 产生数据库查询,教师名字也会被一并查出
    print(s.teacher_name)
  • prefetch_related : 针对多对多关系的数据。
    1
    2
    3
    4
    # 假设学生表和老师表通过外键关联,多对多关系
    students = Student.objects.all().prefetch_related("teacher")
    for s in students:
    print(s.teacher.all()

常用的字段查询

  • contains : 包含,用来相似查询
  • icontains : 同上,只是忽略大小写
  • exact : 精确匹配
  • in : 指定某个集合
  • gt : 大于
  • gte : 大于等于
  • lt : 小于
  • lte : 小于等于
  • startswith : 以某个字符串开始
  • endswith : 以某个字符串结束
  • istartswith : 忽略大小写
  • iendswith : 忽略大小写
  • range : 范围查询,多用于时间。例如Info.objects.filter(created_time__range=('2019-01-01','2020-01-01'))

F查询

F查询主要用来执行数据库层面的计算,从而避免出现竞争状态。
比如处理文章的访问量:

1
2
3
4
from django.db.models import F
article = Article.objects.get(id=1)
article.pv = F('pv') + 1 # 这会在数据库层面执行原子性操作,避免了多线程下脏数据问题
article.save()

也可以用来直接比较值

1
2
3
4
5
6
7
8
# 查询评论数小于收藏数的书籍
Book.objects.filter(commnetNum__lt=F('keepNum'))

# 查询评论数小于收藏数2倍的书籍
Book.objects.filter(commnetNum__lt=F('keepNum')*2)

# 直接在对应字段值上进行加减乘除和取模操作
Book.objects.all().update(price=F("price")+30) 

Q查询

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

你可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询

1
2
3
4
from django.db.models import Q
bookList=Book.objects.filter(Q(authors__name="小明")|Q(authors__name="大刘"))
# 上面的查询语句等同于sql中
WHERE name ="小明" OR name ="大刘"

查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将”AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如:

1
bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017), title__icontains="python")

一些有助于理解的样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 查询评论数大于阅读数的书籍
from django.db.models import F,Q
# select * from book where commit_num>read_num;
# 这样肯定是不行的
# Book.objects.filter(commit_num__gt=read_num)
ret=Book.objects.filter(commit_num__gt=F('reat_num'))
print(ret)

# 把所有书籍的价格加10
Book.objects.all().update(price=F('price')+10)

# Q函数,描述一个与,或,非的关系
# 查询名字叫红楼梦或者价格大于100的书
ret=Book.objects.filter(Q(name='红楼梦')|Q(price__gt=100))
print(ret)
# 查询名字叫红楼梦和价格大于100的书
ret = Book.objects.filter(Q(name='红楼梦') & Q(price__gt=100))
print(ret)
# 等同于
ret2=Book.objects.filter(name='红楼梦',price__gt=100)
print(ret2)


# 也可以Q套Q
# 查询名字叫红楼梦和价格大于100 或者 nid大于2
ret=Book.objects.filter((Q(name='红楼梦') & Q(price__gt=100))|Q(nid__gt=2))
print(ret)

# 非
ret=Book.objects.filter(~Q(name='红楼梦'))
print(ret)
# Q和键值对联合使用,但是键值对必须放在Q的后面(描述的是一个且的关系)
# 查询名字不是红楼梦,并且价格大于100的书
ret=Book.objects.filter(~Q(name='红楼梦'),price__gt=100)
print(ret)
如果觉得写的还行,赞助瓶脉动~
0%