Django QuerySet API 整理

(译自官方文档)

以下操作会真正操作数据库:

  • Iteration:在遍历对象时就会执行数据库操作
  • Slicing:对与一个未查询的 QuerySet 切片就会返回另一个未查询的 QuerySet 。使用参数 step 参数时会执行数据库操作
  • Picking/Caching:参见 Picking QuerySet
  • repr():调用该方法时查询会被运行,并可以立即看到查询结果(命令行常用)
  • len():返回查询结果列表的长度。运行时执行数据库操作,使用 count() 会更加高效
  • list():再转化为列表时会执行数据库操作,且占用大量内存。而遍历 QuerySet 时仅使用某个对象时才加载至内存中

Picking QuerySet

import pickle
query = pickle.loads(s) # Assuming 's' is the pickled string.
qs = MyModel.objects.all()
qs.query = query # Restore the original 'query'.

QuerySet API

  • filter(**kwargs):根据筛选条件返回相匹配的对象。参见 Filed lookups
  • exclued(**kwargs):根据筛选条件返回不匹配的对象。
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')
SELECT ...
WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')
  • annotate(*args,**kwargs):为 QuerySet 中的每个对象添加注解(可以理解为辅助计算列),具体的函数参见 Aggregation Functions
>>> q = Blog.objects.annotate(number_of_entries=Count('entry'))
# The number of entries on the first blog, using the name provided
>>> q[0].number_of_entries
42
  • order_by(*fileds):默认情况下,QuerySet 返回的查询结果以 Model 类的 Meta 设置所提供的 ordering 项中定义的排序元组来排序。可以使用 order_by 进行覆盖。通过设置 QuerySet.ordered 属性,可以决定某个查询是否可排序
# 🌰:以 pub_date 升序,再以 headline 倒序,默认倒序
Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')

# 随机排序(占用较多资源)
Entry.objects.order_by('?')

# 根据其他 Model 类的字段排序,若没有指定字段,默认会采用 Meta.ordering 进行排序。否则会以主键排序
Entry.objects.order_by('blog__name', 'headline')

# 也可以使用 asc() 和 desc() 方法
Entry.objects.order_by(Coalesce('summary', 'headline').desc())

  • reverse():对查询结果进行反向排序
# 得到查询结果中最后五个对象
my_queryset.reverse()[:5]
  • distinct():返回一个新的 QuerySet ,在执行查询时使用 select distinct. 默认情况下,QuerySet 会自动剔除重复的记录,但是在多表查询时,可能会用到 Distinct(慎用)
  • values(*fields):返回一个 ValuesQuerySet ,运行后得到的不是 Model 的实例话对象,而是一个可迭代的字典序列
# This list contains a Blog object.
>>> Blog.objects.filter(name__startswith='Beatles')
[<Blog: Beatles Blog>]

# This list contains a dictionary.
>>> Blog.objects.filter(name__startswith='Beatles').values()
[{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]

# 入参可以指定字段,限定 Select 选取的数据
>>> Blog.objects.values()
[{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}],
>>> Blog.objects.values('id', 'name')
[{'id': 1, 'name': 'Beatles Blog'}]
#
  • values_list(*fields):与 values() 类似,不过返回的是元组序列
>>> Entry.objects.values_list('id', 'headline')
[(1, u'First entry'), ...]

# 如果传递了一个字段做为参数,那么使用 flat 参数(值为 True),就意味着返回结果都是单独的值,而不是元组。如下:
>>> Entry.objects.values_list('id').order_by('id')
[(1,), (2,), (3,), ...]

>>> Entry.objects.values_list('id', flat=True).order_by('id')
[1, 2, 3, ...]

# 注意这个方法返回的是 ValuesListQuerySet对象,和列表相似但并不是列表,需要列表操作时需list()转为列表。
  • dates(field , kind , order=’ASC’):返回一个DateQuerySet,提取 QuerySet 查询中所包含的日期,组成一个新的 datetime.date 对象列表
>>> Entry.objects.dates('pub_date', 'year')
[datetime.date(2005, 1, 1)]
>>> Entry.objects.dates('pub_date', 'month')
[datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)]
>>> Entry.objects.dates('pub_date', 'day')
[datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)]
>>> Entry.objects.dates('pub_date', 'day', order='DESC')
[datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)]
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
[datetime.date(2005, 3, 20)]
  • datetimes(field , kind , order=’ASC’):返回一个 DateTimeQuerySet ,组成一个新的 datetime.datetime 对象列表
  • none():返回一个 EmptyQuerySet ,在运行时只返回一个空列表。适用于你要返回一个空列表,但调用者却需要接收一个 QuerySet 对象。
>>> Entry.objects.none()
[]
>>> from django.db.models.query import EmptyQuerySet
>>> isinstance(Entry.objects.none(), EmptyQuerySet)
True
  • all():返回当前 QuerySet 但一份拷贝。当 QuerySet 运行后,会缓存查询结果,如果数据库发生了改变,调用 all() 可以更新查询
  • select_related():返回一个 QuerySet ,在执行查询时进口可能深入的遍历外键关系,再查询后再使用外键关系获取关联对象时不会再读取数据库(通过库内 Join 进行查询)
  • prefetch_related():与 select_related() 类似,但是是分别查询每个表,然后用 Python 处理他们之间的关系
from django.db import models

class Topping(models.Model):
name = models.CharField(max_length=30)

class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ManyToManyField(Topping)

def __str__(self): # __unicode__ on Python 2
return "%s (%s)" % (self.name, ", ".join([topping.name
for topping in self.toppings.all()]))



>>> Pizza.objects.all().prefetch_related('toppings')


class Restaurant(models.Model):
pizzas = models.ManyToMany(Pizza, related_name='restaurants')
best_pizza = models.ForeignKey(Pizza, related_name='championed_by')



>>> Restaurant.objects.prefetch_related('pizzas__toppings')
>>> Restaurant.objects.prefetch_related('best_pizza__toppings')
>>> Restaurant.objects.select_related('best_pizza').prefetch_related('best_pizza__toppings')


  • extra(select=None,where=None,params=None,tables=None,order_by=None,select_params=None)
    extra() 可以修改 QuerySet ,能在生成的SQL语句中注入新的子句。但应尽量避免写 extra ,因为违背了 DRY 原则
  • defer(*field):将不想载入但字段传给 defer() 方法,可以做到延后载入
Entry.objects.defer("lede", "body")

Entry.objects.defer("body").filter(headline="Lennon").defer("lede")

# 也可以延迟载入关联 Model 中的字段,前提是使用 select_related() 载入了关联 Model
Blog.objects.select_related().defer("entry__lede", "entry__body")

# 清除延后载入设置,传入 None
my_queryset.defer(None)
  • only(*field):和 defer() 作用相反,立即载入可以使用 only()
Person.objects.defer("age", "biography")
Person.objects.only("name")

# 连续调用只有最后一个 only() 会生效
Entry.objects.only("body", "lede").only("headline")

# 可以结合 defer() 一起使用,先 only 后 defer
# Final result is that everything except "headline" is deferred.
Entry.objects.only("headline", "body").defer("body")

# Final result loads headline and body immediately (only() replaces any
# existing set of fields).
Entry.objects.defer("body").only("headline", "body")
  • 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(nowait=False):返回queryset,并将需要更新的行锁定,类似于SELECT … FOR UPDATE的操作
entries = Entry.objects.select_for_update().filter(author=request.user)
  • raw(raw_query, params=None, translations=None):执行 Raw SQL 查询

不返回查询的方法

这些方法不使用缓存,他们在运行时是直接读取数据库的。

  • get(**kwargs):返回与筛选条件相匹配的对象。返回对象超过一个,抛出 MultipleObjectsReturned 异常,没有返回对象返回 DoesNotExist 异常
# DoesNotExist 异常继承自 django.core.exceptions.ObjectDoesNotExist,可以直接截获 DoesNotExist 异常
from django.core.exceptions import ObjectDoesNotExist
try:
e = Entry.objects.get(id=3)
b = Blog.objects.get(id=1)
except ObjectDoesNotExist:
print("Either the entry or blog doesn't exist.")
  • create(**kwargs):创建对象并保存
# 以下操作等效
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")

p = Person(first_name="Bruce", last_name="Springsteen")
p.save(force_insert=True)

# 手动指定主键时,要处理好同主键的异常
  • get_or_create(default=None,**kwargs):根据筛选条件查询对象,若不存在就创建一个
# 以下代码效果相同,知识第一种过于臃肿
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()

obj, created = Person.objects.get_or_create(first_name='John', last_name='Lennon',
defaults={'birthday': date(1940, 10, 9)})

# defaults 只能在创建时对对象赋值,而不能进行查询
Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})
  • update_or_create(defaults=None,**kwargs):与 get_or_create() 类似
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()

# 可以简化为

obj, created = Person.objects.update_or_create(
first_name='John', last_name='Lennon', defaults=updated_values)
  • bulk_create(objs,batch_size=None):批量创建
>>> Entry.objects.bulk_create([
... Entry(headline="Django 1.0 Released"),
... Entry(headline="Django 1.1 Announced"),
... Entry(headline="Breaking: Django is awesome")
... ])

# 优于

Entry.objects.create(headline="Python 1.0 Released"
Entry.objects.create(headline="Python 1.1 Planned"
  • count():返回数据库中匹配查询的对象数量,不会抛出任何异常
  • in_bulk(id_list):接收一个主键值列表,然后根据每个主键值所其对应的对象,返回一个主键值与对象的映射字典
>>> 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=None):根据时间字段 field_name 得到最新的对象
Entry.objects.latest('pub_date')
  • earliest(field_name=None):根据时间字段 field_name 得到最旧(早)的对象

  • first():排序后对第一个对象

    p = Article.objects.order_by('title', 'pub_date').first()
  • last():类似于 first()

  • aggregate(*args, **kwargs):通过对 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():查询是否有值,返回布尔值
  • update():更新
  • delete():删除

字段筛选条件(Field Lookups)

字段筛选条件决定了你如何构造 SQL 语句中的 WHERE 从句。它们被指定为 QuerySet 中 filter(),exclude() 和 get() 方法的关键字参数。

  • exact():精确匹配,指定为 None 值会翻译为 SQL 中对 Null
Entry.objects.get(id__exact=14)
Entry.objects.get(id__exact=None)
  • iexact():忽略大小写的匹配,SQLite 对 Unicode 字符串,无法做忽略大小写的匹配。
Blog.objects.get(name__iexact='beatles blog')
Blog.objects.get(name__iexact=None)
  • contains():大小写敏感的包含匹配
Entry.objects.get(headline__contains='Lennon')
  • icontains():忽略大小写敏感的包含匹配
Entry.objects.get(headline__icontains='Lennon')
  • in:是否在给定的列表中
Entry.objects.filter(id__in=[1, 3, 4])

inner_qs = Blog.objects.filter(name__contains='Cheddar')
entries = Entry.objects.filter(blog__in=inner_qs)

inner_qs = Blog.objects.filter(name__contains='Ch').values('name')
entries = Entry.objects.filter(blog__name__in=inner_qs)

  • gt:大于
  • gte:大于等于
  • lt:小于
  • lte:小于等于
  • startswith:大小写敏感的以**开头
  • istartswith:忽略大小写的以**开头
  • endswith:大小写敏感的以**结尾
  • iendswith:忽略大小写的以**结尾
  • range:包含的范围,类似 SQL 中的 BETWEEN
import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
Entry.objects.filter(pub_date__range=(start_date, end_date))
  • year:对日期/时间字段精确匹配年份,年份用四位数字表示
Entry.objects.filter(pub_date__year=2005)

等价于SQL:

SELECT ... WHERE EXTRACT('year' FROM pub_date) = '2005';
  • month:对日期/时间字段精确匹配月份
  • day:对日期/时间字段精确匹配日期
  • week_day:对日期/时间字段精确匹配周数
  • hour:对日期/时间字段精确匹配小时
  • minute:对日期/时间字段精确匹配分钟
  • second:对日期/时间字段精确匹配秒
  • isnull:根据字段是否为空筛选数据
Entry.objects.filter(pub_date__isnull=True)
SELECT ... WHERE pub_date IS NULL;
  • search:利用全文索引做全文搜索,与 contains 类似,但使用全文索引会更快
Entry.objects.filter(headline__search="+Django -jazz Python")
  • regex:大小写敏感的正则表达式匹配
Entry.objects.get(title__regex=r'^(An?|The) +')
  • iregex:忽略大小写的正则表达式匹配

聚合函式

  • avg:__avg ,返回所给字段的平均值
  • count:__count ,返回所给关联字段的 model 数量
  • max:__max ,返回所给字段数据的最大值
  • min:__min , 返回所给字段数据的最小值
  • strdev:__stddev ,返回所给字段的标准差,sample=True 返回一个样本偏差值
  • sum:__sum ,计算所给字段的和
  • variance:__variance ,计算所给字段的标准方差,sample=True 返回样本方差