QuerySet API 学习笔记

2018.08.19

QuerySets evaluated

可以创建、过滤、切片和传递查询集而不用真实操作数据库。在你对查询集做求值之前,不会发生任何实际的数据库操作。

对于查询集我们使用以下方法进行求值:

  • Iteration
  • Slicing
  • Pickling/Caching
  • repr()
  • len()
  • list()
  • bool()

QuerySet API

返回新的查询集方法

  • filter

返回一个新的 QuerySet 包含给定参数的查询匹配对象,更复杂的查询可以使用 Q 对象。

  • exclude

返回一个新的 QuerySet,它包含不满足给定的查找参数的对象,更复杂的查询同样可以使用 Q 对象。

  • annotate

使用提供的查询表达式给 QuerySet 的每个对象增加注释,可以理解为增加一个新的属性。

查询表达式可以是一个简单的值、模型(或关联模型)字段的一个引用或对查询集中的对象一个聚合函数(平均值、和等)

>>> from django.db.models import Count
>>> q = Blog.objects.annotate(Count('entry'))
>>> q[0].name
'Blogasaurus'
>>> q[0].entry__count # 新增的 entry__count 属性
42

# 指定注释名称
>>> q = Blog.objects.annotate(number_of_entries=Count('entry'))
>>> q[0].number_of_entries
42

使用 annotate 需要用 django.db.models 的统计方法。除了 Count 统计方法之外,还有 Max、Min、Sum、Avg 等方法。

  • order_by

指定 QuerySet 排序

# 升序
>>> Author.objects.filter().order_by('name')
<QuerySet [<Author: abner>, <Author: mike>]>
 
# 降序
>>> Author.objects.filter().order_by('-name')
<QuerySet [<Author: mike>, <Author: abner>]>

# 随机排序,不推荐使用(速度慢、查询昂贵)
>>> Author.objects.order_by('?')
  • reverse

反向排序 QuerySet 中返回的元素

  • distinct

返回一个新的 QuerySet 并去除查询结果中的重复的行

>>> CompileRules.objects.values('business__name').distinct()
<QuerySet [{'business__name': 'img.ppdai.com'}, {'business__name': 'api.alert.risk.ppdaicorp.com'}, {'business__name': 'api.antifraud.risk.ppdaicorp.com'}, {'business__name': 'chargingjob.ppdapi.com'}]>

>>> rules = CompileRules.objects.distinct().values_list('business__name')
>>> [i[0] for i in rules]
['img.ppdai.com','api.alert.risk.ppdaicorp.com','api.antifraud.risk.ppdaicorp.com','chargingjob.ppdapi.com']
  • values

把 QuerySets 当作迭代器使用,返回一个字典,而不是模型实例

>>> Author.objects.filter(name='abner').values()
<QuerySet [{'id': 2, 'name': 'abner', 'email': 'abner@163.com'}, {'id': 3, 'name': 'abner', 'email': 'abner@qq.com'}]>


# 指定字段
>>> Author.objects.values('name')
<QuerySet [{'name': 'abner'},{'name': 'abner'},v{'name': 'mike'}]>


# 数据库函数,Lower 返回小写形式
>>> from django.db.models.functions import Lower
>>> Author.objects.values(lower=Lower('name'))
<QuerySet [{'lower': 'dkey'}, {'lower': 'jerry'}, {'lower': 'jerry'}]>
  • values_list

以元组的形式返回查询集,可以返回特定字段的值,也可以是列表的形式

>>> Business.objects.filter(level=1).values_list('id', 'name')
<TreeQuerySet [(3, '借入运营'), (106, '借入借贷'), (219, '数据决策平台'), (225, '数据架构'), (234, '测试')]>

>>> Business.objects.filter(level=1).values_list('name', flat=True)
<TreeQuerySet ['借入运营', '借入借贷', '数据决策平台', '数据架构', '测试']>
  • dates

dates(field, kind, order='ASC’)

返回 datetime.date 对象列表,查询的字段应该是 DateField 模型,kind应为"year”、“month"或"day”。隐式的是升序排序

1.year:返回字段不同年份值列表
2.month:返回字段所有不同年/月值列表
3.day:返回字段所有不同年/月/日值列表
  • datetimes

datetimes(field_name, kind, order='ASC’, tzinfo=None)

返回 datetime.datetimes 对象列表,查询的字段应该是 DateField 模型,kind应为"year”、“month"或"day”。隐式的是升序排序

  • none

创建一个不返回任何对象的查询集,访问结果时不会执行任何查询。

>>> Entry.objects.none()
[]
>>> from django.db.models.query import EmptyQuerySet
>>> isinstance(Entry.objects.none(), EmptyQuerySet)
True
  • all

所有查询集

  • select_related

对于 ForeignKey 和 OneToOneField 等字段,通过添加 select_related,可以把相关的对象在一次查询中查出,之后使用时就不需要再次查数据库

返回一个新的查询集,沿着外键查询关联对象的数据。它会生成一个复杂的查询并引起性能的损耗,但是在以后使用外键关系时将不需要数据库查询。

# 普通查询
# Hits the database.
e = Entry.objects.get(id=5)
# Hits the database again to get the related Blog object.
b = e.blog

#select_related 查询 
# Hits the database.
e = Entry.objects.select_related('blog').get(id=5)
# Doesn't hit the database, because e.blog has been prepopulated
# in the previous query.
b = e.blog
  • prefetch_related

prefetch_related 对于相关对象会进行一次独立的查询,然后在 Python 中把对象关联起来。所以prefetch_related可以用于many-to-many and many-to-one关系

  • extra

extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

有些情况下,Django的查询语法难以简单的表达复杂的 WHERE 子句,对于这种情况, Django 提供了 extra() QuerySet 修改机制 — 它能在 QuerySet生成的SQL从句中注入新子句

extra可以指定一个或多个参数,例如 select, where or tables。 这些参数都不是必须的,但是你至少要使用一个

  • defer

排除不需要的字段,降低性能损耗

  • only

仅选择需要的字段

  • using

using(alias) // alias 数据库别名

如果使用多个数据库,改方法可以控制从哪个数据库上求值。

# queries the database with the 'default' alias.
>>> Entry.objects.all()

# queries the database with the 'backup' alias
>>> Entry.objects.using('backup')
  • select_for_update

返回一个 queryset ,会锁定相关行直到事务结束

entries = Entry.objects.select_for_update().filter(author=request.user)

所有匹配的行将被锁定,直到事务结束。这意味着可以通过锁防止数据被其它事务修改。

  • raw

接收一个原始的SQL 查询,执行它并返回一个django.db.models.query.RawQuerySet 实例

Django提供两种方法使用原始SQL进行查询:一种是使用Manager.raw()方法,进行原始查询并返回模型实例;另一种是完全避开模型层,直接执行自定义的SQL语句。

>>> raw = Author.objects.raw('select * from polls_author')
>>> type(raw)
<class 'django.db.models.query.RawQuerySet'>

不返回新的查询集

  • get

返回按照参数匹配的对象

  • create

创建并保存对象

  • get_or_create

通过给定参数来查询对象,如果对象不存在则会创建一个新的对象

返回一个由(object, created)组成的元组,元组中的object 是一个查询到的或者是被创建的对象, created 是一个表示是否创建了新的对象的布尔值。

try:
    obj = Person.objects.get(first_name='John', last_name='Lennon')
except Person.DoesNotExist:
    obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
    obj.save()
    
# use get_or_create
obj, created = Person.objects.get_or_create(first_name='John', last_name='Lennon',
                  defaults={'birthday': date(1940, 10, 9)})

任何传递给 get_or_create() 的关键字参数,除了一个可选的defaults,都将传递给get() 调用

get_or_create() 在Django 视图中的使用。请确保只在POST 请求中使用,除非你有充分的理由。GET 请求不应该对数据有任何影响。而POST 则用于对数据产生影响的请求

  • update_or_create

通过给定参数来更新对象,如果对象不存在则会创建一个新的对象

一个通过给出的kwargs 来更新对象的便捷方法, 如果需要的话创建一个新的对象。defaults 是一个由 (field, value) 对组成的字典,用于更新对象

try:
    obj = Person.objects.get(first_name='John', last_name='Lennon')
    for key, value in updated_values.iteritems():
        setattr(obj, key, value)
    obj.save()
except Person.DoesNotExist:
    updated_values.update({'first_name': 'John', 'last_name': 'Lennon'})
    obj = Person(**updated_values)
    obj.save()
    

# use update_or_create
obj, created = Person.objects.update_or_create(
    first_name='John', last_name='Lennon', defaults=updated_values)    

和上文描述的get_or_create() 一样,这个方式容易导致竞态条件,如果数据库层级没有前置唯一性它会让多行同时插入。

  • bulk_create

批量写入数据

  • count

返回 QuerySet 对象个数

  • in_bulk

获取主键值的列表,并返回将每个主键值映射到具有给定ID的对象的实例的字典。

>>> Blog.objects.in_bulk([1])
{1: <Blog: Beatles Blog>}
>>> Blog.objects.in_bulk([1, 2])
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>}
>>> Blog.objects.in_bulk([])
{}
  • iterator
  • latest:使用作为日期字段提供的field_name,按日期返回表中的最新对象
  • earliest
  • first
  • last
  • aggregate:聚合查询

返回一个字典,包含根据QuerySet 计算得到的聚合值(平均数、和等等)。aggregate() 的每个参数指定返回的字典中将要包含的值

>>> from django.db.models import Count
>>> q = Blog.objects.aggregate(Count('entry'))
{'entry__count': 16}

>>> q = Blog.objects.aggregate(number_of_entries=Count('entry'))
{'number_of_entries': 16}
  • exists

exists() 用于搜寻对象是否在QuerySet 中以及QuerySet 是否存在任何对象,特别是QuerySet 比较大的时候。

# exists 方法比普通方法快
entry = Entry.objects.get(pk=123)
if some_queryset.filter(pk=entry.pk).exists():
    print("Entry contained in queryset")
    
# 普通方法
if entry in some_queryset:
   print("Entry contained in QuerySet")    
   
# 判断 queryset 是否含有对象,exists 比普通判断快特别是查询集比较大的情况下   
if some_queryset.exists():
    print("There is at least one object in some_queryset")
    
if some_queryset:
    print("There is at least one object in some_queryset")    
  • update

对指定的字段执行SQL更新查询,并返回匹配的行数

  • delete

对QuerySet中的所有行执行SQL删除查询

  • as_manager

字段查找

方法 含义
exact 精确匹配
iexact 忽略大小写精确匹配
contains 包含关系
icontains 忽略大小写包含关系
in 在给定的列表
gt 大于
gte 大于等于
lt 小于
lte 小于等于
startswith 区分大小写,从开始位置匹配
istartswith 不区分大小写,从开始位置匹配
endswith 区分大小写,从结束位置匹
iendswith 不区分大小写,从结束位置匹配
range 范围测试(包含于之中)
year 对于日期和日期时间字段,年份匹配
month 对于日期和日期时间字段,月份匹配
day 对于日期和日期时间字段,天数匹配
week_day 星期匹配
hour 对于日期时间字段,精确的小时匹配
minute 对于日期时间字段,精确的分钟匹配
second 对于datetime字段,精确的秒匹配
isnull 判断字段值为 True 或 False
search 一个Boolean类型的全文搜索,以全文搜索的优势
regex 正则表达式
iregex 不区分大小写的正则表达式匹配

参考

官方文档

Django 1.8 中文文档