查询集API

阅读: 76857     评论:14

本节将详细介绍查询集的API,它建立在下面的模型基础上,与上一节的模型相同:

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=200)
    email = models.EmailField()

    def __str__(self):
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    authors = models.ManyToManyField(Author)
    number_of_comments = models.IntegerField()
    number_of_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):
        return self.headline

一、QuerySet何时被提交

在内部,创建、过滤、切片和传递一个QuerySet不会真实操作数据库,在你对查询集提交之前,不会发生任何实际的数据库操作。

可以使用下列方法对QuerySet提交查询操作:

  • 迭代

QuerySet是可迭代的,在首次迭代查询集时执行实际的数据库查询。 例如, 下面的语句会将数据库中所有Entry的headline打印出来:

for e in Entry.objects.all():
    print(e.headline)

# 不要用循环来判断某个元素是否存在,建议使用exists()方法
  • 切片:如果使用切片的”step“参数,Django 将执行数据库查询并返回一个列表。

  • Pickling/缓存:当进行序列化或者缓存的时候

  • repr():打印或者交互的时候

  • len():当你对QuerySet调用len()时, 将提交数据库操作。

  • list():对QuerySet调用list()将强制提交操作entry_list = list(Entry.objects.all())

  • bool()

测试布尔值,像这样:

if Entry.objects.filter(headline="liujiangblog"):
   print("There is at least one Entry with the headline liujiangblog")

注:如果你需要知道是否存在至少一条记录(而不需要真实的对象),使用exists() 将更加高效。

二、QuerySet

下面是对于QuerySet的正式定义:

class QuerySet(model=None, query=None, using=None, hints=None)

QuerySet类具有两个公有属性用于内省:

ordered:如果QuerySet是排好序的则为True,否则为False。

db:当前使用的数据库。

还有一个query属性,可以查看具体执行的SQL语句是怎么写的(需要打印出来)。比如:

print(Blog.objects. all().query)

三、返回新QuerySets的API

大多数QuerySets(不是所有)的方法都会返回一个新的QuerySet,这是为了实现链式调用而设计的。

以下的方法都将返回一个新的QuerySets。重点是加粗的几个API,其它的使用场景很少。

方法名 解释
filter() 过滤查询对象。
exclude() 排除满足条件的对象
annotate() 为查询集添加注解或者聚合内容
order_by() 对查询集进行排序
reverse() 反向排序
distinct() 对查询集去重
values() 返回包含对象具体值的字典的QuerySet
values_list() 与values()类似,只是返回的是元组而不是字典。
dates() 根据日期获取查询集
datetimes() 根据时间获取查询集
none() 创建空的查询集
all() 获取所有的对象
union() 并集
intersection() 交集
difference() 差集
select_related() 附带查询关联对象,利用缓存提高效率
prefetch_related() 预先查询,提高效率
extra() 将被废弃的方法
defer() 不加载指定字段,也就是排除一些列的数据
only() 只加载指定的字段,仅选择需要的字段
using() 选择数据库
select_for_update() 锁住选择的对象,直到事务结束。
raw() 接收一个原始的SQL查询

1. filter()

filter(**kwargs)

返回满足查询参数的对象集合。

查找的参数(**kwargs)应该满足下文字段查找中的格式。多个参数之间是和AND的关系。

2. exclude()

exclude(**kwargs)

返回一个新的QuerySet,它包含满足给定的查找参数的对象。

查找的参数(**kwargs)应该满足下文字段查找中的格式。多个参数通过AND连接,然后所有的内容放入NOT() 中。

下面的示例排除所有pub_date晚于2005-1-3headline为“Hello” 的记录:

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')

下面的示例排除所有pub_date晚于2005-1-3或者headline 为“Hello” 的记录:

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello')

3. annotate()

annotate(args, *kwargs)

注解查询集。

具体内容看《注解和聚合》章节。

注解用于为查询集中的每个对象添加额外的统计信息属性。

例如,如果正在操作一个Blog列表,你可能想知道每个Blog有多少Entry:

>>> from django.db.models import Count
>>> q = Blog.objects.annotate(Count('entry'))
# 第一个博客的名字
>>> q[0].name
'Blogasaurus'
# 第一个博客下面文章的数量
>>> q[0].entry__count
42

Blog模型本身没有定义entry__count属性,但是通过使用一个关键字参数来指定聚合函数,可以控制Annotation的名称:

>>> 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

4. order_by()

默认情况下,根据模型的Meta类中的ordering属性对QuerySet中的对象进行排序。

可以通过查询时的order_by()改变上述默认行为,指定新的排序依据:

Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')

上面的结果将按照pub_date降序排序,然后再按照headline升序排序。"-pub_date"前面的负号表示降序顺序。 升序是默认的。 要随机排序,使用"?",如下所示:

Entry.objects.order_by('?')

注:order_by('?')可能耗费资源且很慢,这取决于使用的数据库。

若要按照另外一个模型中的字段排序,可以使用查询关联模型的语法。即通过字段的名称后面跟两个下划线(__),再加上新模型中的字段的名称,直到希望连接的模型。 像这样:

Entry.objects.order_by('blog__name', 'headline')

如果排序的字段与另外一个模型关联,Django将使用关联的模型的默认排序,或者如果没有指定Meta.ordering将通过关联的模型的主键排序。 例如,因为Blog模型没有指定默认的排序:

Entry.objects.order_by('blog')

与以下相同:

Entry.objects.order_by('blog__id')

如果Blog设置了ordering = ['name'],那么第一个QuerySet将等同于:

Entry.objects.order_by('blog__name')

还可以通过调用表达式的desc()或者asc()方法:

Entry.objects.order_by(Coalesce('summary', 'headline').desc())

考虑下面的情况,指定一个多值字段来排序(例如,一个ManyToManyField 字段或者ForeignKey 字段的反向关联):

class Event(Model):
   parent = models.ForeignKey(
       'self',
       on_delete=models.CASCADE,
       related_name='children',
   )
   date = models.DateField()

Event.objects.order_by('children__date')

在这里,每个Event可能有多个排序数据;具有多个children的每个Event将被多次返回到order_by()创建的新的QuerySet中。 换句话说,用order_by()方法对QuerySet对象进行操作会返回一个扩大版的新QuerySet对象。因此,使用多值字段对结果进行排序时要格外小心。

没有方法指定排序是否考虑大小写。 对于大小写的敏感性,Django将根据数据库中的排序方式排序结果。

可以通过Lower将一个字段转换为小写来排序,它将达到大小写一致的排序:

Entry.objects.order_by(Lower('headline').desc())

可以通过检查QuerySet.ordered属性来知道查询是否是排序的。

每个order_by()都将清除前面的任何排序。 例如下面的查询将按照pub_date排序,而不是headline

Entry.objects.order_by('headline').order_by('pub_date')

5. reverse()

反向排序QuerySet中返回的元素。 第二次调用reverse()将恢复到原有的排序。

要获取QuerySet中最后五个元素,可以这样做:

my_queryset.reverse()[:5]

这与Python直接使用负索引有点不一样。 Django不支持负索引,只能曲线救国。

6. distinct()

distinct(*fields)

去除查询结果中重复的行。

默认情况下,QuerySet不会去除重复的行。当查询跨越多张表的数据时,QuerySet可能得到重复的结果,这时候可以使用distinct()进行去重。

只有当你使用的是PostgreSQL 数据库的时候,才可以为distinct方法指定字段参数,并且需要在前面进行以下order_by调用。order_by的字段参数要在distinct字段参数的开始位置重复。如下所示:

>>> Author.objects.distinct()
[...]

>>> Entry.objects.order_by('pub_date').distinct('pub_date')
[...]

>>> Entry.objects.order_by('blog').distinct('blog')
[...]

>>> Entry.objects.order_by('author', 'pub_date').distinct('author', 'pub_date')
[...]

>>> Entry.objects.order_by('blog__name', 'mod_date').distinct('blog__name', 'mod_date')
[...]

>>> Entry.objects.order_by('author', 'pub_date').distinct('author')
[...]

7. values()

values(fields, *expressions)

返回一个包含数据字典的queryset,而不是模型实例。

每个字典表示一个对象,键对应于模型对象的属性名称。

你可以简单地理解为Python字典的values。

下面的例子将values() 与普通的模型对象进行比较:

# 列表中包含的是Blog对象
>>> Blog.objects.filter(name__startswith='Beatles')
<QuerySet [<Blog: Beatles Blog>]>
# 列表中包含的是数据字典
>>> Blog.objects.filter(name__startswith='Beatles').values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>

该方法接收可选的位置参数*fields,它指定values()应该显示哪些字段。如果指定字段,每个字典将只包含指定的字段的键/值。如果没有指定字段,每个字典将包含数据库表中所有字段的键和值。

例如:

>>> Blog.objects.values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
>>> Blog.objects.values('id', 'name')
<QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>

values()方法还有关键字参数**expressions,这些参数将传递给annotate()

>>> from django.db.models.functions import Lower
>>> Blog.objects.values(lower_name=Lower('name'))
<QuerySet [{'lower_name': 'beatles blog'}]>

在values()子句中的聚合应用于相同values()子句中的其他参数之前。 如果需要按另一个值分组,请将其添加到较早的values()子句中。 像这样:

>>> from django.db.models import Count
>>> Blog.objects.values('author', entries=Count('entry'))
<QuerySet [{'author': 1, 'entries': 20}, {'author': 1, 'entries': 13}]>
>>> Blog.objects.values('author').annotate(entries=Count('entry'))
<QuerySet [{'author': 1, 'entries': 33}]>

注意:

如果你有一个字段foo是一个ForeignKey,默认的foo_id参数返回的字典中将有一个叫做foo 的键,因为这是保存实际值的那个隐藏的模型属性的名称。 当调用foo_id并传递字段的名称,传递foo 或values()都可以,得到的结果是相同的。像这样:

>>> Entry.objects.values()
<QuerySet [{'blog_id': 1, 'headline': 'First Entry', ...}, ...]>
>>> Entry.objects.values('blog')
<QuerySet [{'blog': 1}, ...]>
>>> Entry.objects.values('blog_id')
<QuerySet [{'blog_id': 1}, ...]>

当values()与distinct()一起使用时,注意排序可能影响最终的结果。

如果values()子句位于extra()调用之后,extra()中的select参数定义的字段必须显式包含在values()调用中。 values( 调用后面的extra( 调用将忽略选择的额外的字段。

在values()之后调用only()和defer()不太合理,所以将引发一个NotImplementedError。

可以通过ManyToManyField、ForeignKey 和 OneToOneFiel 属性反向引用关联的模型的字段:

>>> Blog.objects.values('name', 'entry__headline')
<QuerySet [{'name': 'My blog', 'entry__headline': 'An entry'},
     {'name': 'My blog', 'entry__headline': 'Another entry'}, ...]>

8. values_list()

values_list(*fields, flat=False, named=False)

与values()类似,只是在迭代时返回的是元组而不是字典。每个元组包含传递给values_list()调用的相应字段或表达式的值,因此第一个项目是第一个字段等。 像这样:

>>> Entry.objects.values_list('id', 'headline')
<QuerySet [(1, 'First entry'), ...]>
>>> from django.db.models.functions import Lower
>>> Entry.objects.values_list('id', Lower('headline'))
<QuerySet [(1, 'first entry'), ...]>

如果只传递一个字段,还可以传递flat参数。 如果为True,它表示返回的结果为单个值而不是元组。 如下所示:

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

如果有多个字段,传递flat将发生错误。

可以传递named=True参数,让返回值变成一个namedtuple命名元组类型:

>>> Entry.objects.values_list('id', 'headline', named=True)
<QuerySet [Row(id=1, headline='First entry'), ...]>

如果不传递任何值给values_list(),它将返回模型中的所有字段,以在模型中定义的顺序。

常见的情况是获取某个模型实例的特定字段值。可以使用values_list(),然后调用get():

>>> Entry.objects.values_list('headline', flat=True).get(pk=1)
'First entry'

values()values_list()都用于特定情况下的优化:检索数据子集,而无需创建模型实例。

注意通过ManyToManyField进行查询时的行为:

>>> Author.objects.values_list('name', 'entry__headline')
<QuerySet [('Noam Chomsky', 'Impressions of Gaza'),
 ('George Orwell', 'Why Socialists Do Not Believe in Fun'),
 ('George Orwell', 'In Defence of English Cooking'),
 ('Don Quixote', None)]>

类似地,当查询反向外键时,对于没有任何作者的条目,返回None。

>>> Entry.objects.values_list('authors')
<QuerySet [('Noam Chomsky',), ('George Orwell',), (None,)]>

9. dates()

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

返回一个QuerySet,表示QuerySet内容中特定类型的所有可用日期的datetime.date对象列表。

field参数是模型的DateField的名称。 kind参数应为"year","month"或"day"。 结果列表中的每个datetime.date对象被截取为给定的类型。

  • "year" 返回对应该field的所有不同年份值的列表。

  • "month"返回字段的所有不同年/月值的列表。

  • "day"返回字段的所有不同年/月/日值的列表。

order参数默认为'ASC',或者'DESC'。 它指定如何排序结果。

例子:

>>> 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)]

10. datetimes()

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

返回的QuerySet为datetime.datetime对象的列表,表示QuerySet内容中特定种类的所有可用日期。

field_name应为模型中DateTimeField类型字段的名称。

kind参数应为"hour","minute","month","year","second"或"day"。

结果列表中的每个datetime.datetime对象被截取到给定的类型。

order参数默认为'ASC',或者'DESC'。 它指定如何排序结果。

tzinfo参数定义在截取之前将数据时间转换到的时区。

11. none()

调用none()将创建一个不返回任何对象的查询集,并且在访问结果时不会执行任何查询。

例子:

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

12. all()

返回当前QuerySet(或QuerySet子类)的副本。通常用于获取全部QuerySet对象。

13. union()

union(*other_qs, all=False)

集合中并集的概念!

使用SQL的UNION运算符组合两个或更多个QuerySet的结果。例如:

>>> qs1.union(qs2, qs3)

默认情况下,UNION操作符仅选择不同的值。 要允许重复值,请使用all=True参数。

提示:这三个集合操作,可以针对不同的模型QuerySet,Django在后台会自动进行关联和转换,但其结果往往不是你想要的,弊大于利,慎用!

14. intersection()

intersection(*other_qs)

集合中交集的概念!

使用SQL的INTERSECT运算符返回两个或更多个QuerySet的共有元素。例如:

>>> qs1.intersection(qs2, qs3)

15. difference()

difference(*other_qs)

集合中差集的概念!

使用SQL的EXCEPT运算符只保留原QuerySet中的元素,但不保留其他QuerySet中的元素。例如:

>>> qs1.difference(qs2, qs3)

16. select_related()

select_related(*fields)

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

核心要点:

  • select_related不会改变原始查询语句返回的对象和内容
  • select_related只是请求搭个车,帮忙把关联对象同时查询一下并缓存
  • 在你预料到会对关联对象进行访问的时候,使用select_related可以减少数据库访问次数,加速一下运行效率

下面的例子解释了普通查询和select_related()查询的区别。 下面是一个标准的查询:

# 访问数据库。
e = Entry.objects.get(id=5)
# 再次访问数据库以得到关联的Blog对象。
b = e.blog

下面是一个select_related查询:

# 访问数据库。
e = Entry.objects.select_related('blog').get(id=5)
# 不会访问数据库,因为e.blog已经在前面的查询中获得了。
b = e.blog

select_related()可用于任何查询操作:

from django.utils import timezone

# 查找所有的博客,这些博客中有等待未来发布的文章
blogs = set()

for e in Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog'):
    # 没有select_related(),下面的语句将为每次循环迭代生成一个数据库查询,以获得每个entry关联的blog。
    blogs.add(e.blog)

filter()select_related()的顺序不重要。 下面的查询集是等同的:

Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog')
Entry.objects.select_related('blog').filter(pub_date__gt=timezone.now())

可以沿着外键查询。 如果有以下模型:

from django.db import models

class City(models.Model):
    # ...
    pass

class Person(models.Model):
    # ...
    hometown = models.ForeignKey(
        City,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )

class Book(models.Model):
    # ...
    author = models.ForeignKey(Person, on_delete=models.CASCADE)

调用Book.objects.select_related('author__hometown').get(id=4)将缓存相关的Person 和相关的City:

b = Book.objects.select_related('author__hometown').get(id=4)
p = b.author         # 不访问数据库
c = p.hometown       # 不访问数据库
b = Book.objects.get(id=4) # 没用select_related()的时候
p = b.author         # 访问数据库
c = p.hometown       # 访问数据库

在传递给select_related()的字段中,可以使用任何ForeignKey和OneToOneField。

在传递给select_related的字段中,还可以反向引用OneToOneField。也就是说,可以回溯到定义OneToOneField 的字段。 此时,可以使用关联对象字段的related_name,而不要指定字段的名称。

17. prefetch_related()

prefetch_related(*lookups)

在单个批处理中自动检索每个指定查找的相关对象。

select_related类似,但是策略是完全不同的。

假设有这些模型:

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):
        return "%s (%s)" % (
            self.name,
            ", ".join(topping.name for topping in self.toppings.all()),
        )

并运行:

>>> Pizza.objects.all()
["Hawaiian (ham, pineapple)", "Seafood (prawns, smoked salmon)"...

关键在于Pizza模型的__str__方法!在你执行并打印Pizza.objects.all()的每个对象的时候,都会调用__str__方法。而这个方法里有个join操作,需要连接self.toppings.all()。这就会导致一次次的数据库访问,效率极其低下。

为了解决上面的问题,可以使用prefetch_related减少为只有两次查询:

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

这意味着现在每次self.toppings.all()被调用时,不会再去数据库查找,而是在一个预取的QuerySet缓存中查找。

还可以使用正常连接语法来执行相关字段的相关字段。 假设在上面的例子中增加一个额外的模型:

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

以下是合法的:

>>> Restaurant.objects.prefetch_related('pizzas__toppings')

这将预取所有属于餐厅的比萨饼,和所有属于那些比萨饼的配料。 这将导致总共3个查询 - 一个用于餐馆,一个用于比萨饼,一个用于配料。

>>> Restaurant.objects.prefetch_related('best_pizza__toppings')

这将获取最好的比萨饼和每个餐厅最好的披萨的所有配料。 这将在3个表中查询 - 一个为餐厅,一个为“最佳比萨饼”,一个为一个为配料。

当然,也可以使用best_pizza来获取select_related关系,以将查询数减少为2:

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

18. extra()

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

有些情况下,Django的查询语法难以简单地表达复杂的WHERE子句,对于这种情况,可以在extra()生成的SQL从句中注入新子句。使用这种方法作为最后的手段,这是一个旧的API,在将来的某个时候可能被弃用。仅当无法使用其他查询方法表达查询时才使用它。

例如:

>>> qs.extra(
...     select={'val': "select col from sometable where othercol = %s"},
...     select_params=(someparam,),
... )

相当于:

>>> qs.annotate(val=RawSQL("select col from sometable where othercol = %s", (someparam,)))

19. defer()

在一些复杂的数据建模情况下,模型可能包含大量字段,其中一些可能包含大尺寸数据(例如文本字段),将它们转换为Python对象需要花费很大的代价。

当最初查询数据时,我们不知道是否需要这些特定字段,可以告诉Django先不要从数据库中检索它们。

通过传递字段名称到defer()实现不加载指定的字段:

Entry.objects.defer("headline", "body")

查询动作仍将返回模型实例,并且被排除的字段依然可以调用。这种行为非常类似延迟加载,或者懒加载机制。我先把简单容易的取出来,等需要的时候再取难的大的。

每个被排除的字段将在你访问该字段时从数据库中进行一次额外的检索(每次只检索一个字段,而不是一次检索所有的延迟字段)。

可以多次调用defer()。 每个调用都向延迟集添加新字段:

# 延迟body和headline两个字段。
Entry.objects.defer("body").filter(rating=5).defer("headline")

字段添加到延迟集的顺序无关紧要。对已经延迟的字段名称再次defer()没有问题(该字段仍将被延迟)。

可以使用标准的双下划线符号来分隔关联的字段,从而延迟加载关联模型中的字段:

Blog.objects.select_related().defer("entry__headline", "entry__body")

如果要清除延迟字段集,将None作为参数传递到defer():

# 立即加载所有的字段。
my_queryset.defer(None)

20. only()

only()方法与defer()相反。

only的意思是只有我指定的字段立刻加载,剩下的全部延迟!

假设有一个包含字段biography、age和name的模型。 以下两个查询集是相同的,就延迟字段而言:

Person.objects.defer("age", "biography")
Person.objects.only("name")

每当你调用only()时,它将替换前面即将加载的字段集。因此,对only()的连续调用的结果是只有最后一次调用的字段被考虑:

# 只有headline被立即jian
Entry.objects.only("body", "rating").only("headline")

由于defer()以递增方式动作(向延迟列表中添加字段),因此你可以结合only()和defer()调用:

# 最后只有"headline"被立即检索
Entry.objects.only("headline", "body").defer("body")
# 最后只有headline和body被立即检索
Entry.objects.defer("body").only("headline", "body")

当对具有延迟字段的实例调用save()时,仅保存加载的字段。

21. using()

using(alias)

如果正在使用多个数据库,这个方法用于指定在哪个数据库上查询QuerySet。方法的唯一参数是数据库的别名,定义在DATABASES。

例如:

# 使用'default' 数据库
>>> Entry.objects.all()
# 使用 'backup' 数据库
>>> Entry.objects.using('backup')

22. select_for_update()

select_for_update(nowait=False, skip_locked=False, of=())

返回一个锁住行直到事务结束的查询集,如果数据库支持,它将生成一个SELECT ... FOR UPDATE语句。

例如:

rom django.db import transaction

entries = Entry.objects.select_for_update().filter(author=request.user)
with transaction.atomic():
    for entry in entries:
        ...

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

一般情况下如果其他事务锁定了相关行,那么本查询将被阻塞,直到锁被释放。使用select_for_update(nowait=True)将使查询不阻塞。如果其它事务持有冲突的锁,那么查询将引发DatabaseError异常。也可以使用select_for_update(skip_locked=True)忽略锁定的行。nowait和skip_locked是互斥的。

目前,postgresql,oracle和mysql数据库后端支持select_for_update()

23. raw()

raw(raw_query, params=None, translations=None)

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

这个RawQuerySet实例可以迭代,就像普通的QuerySet一样。

除了以上API,还有两个特殊的组合功能。

24.&

使用AND将两个QuerySet的对象组合起来,也就是交集的意思。QuerySet的模型必须是同样的,不能组合不同模型的查询集。

下面三种方式结果等同:

Model.objects.filter(x=1) & Model.objects.filter(y=2)

Model.objects.filter(x=1, y=2)

from django.db.models import Q
Model.objects.filter(Q(x=1) & Q(y=2))

25.|

使用OR将两个QuerySet的对象组合起来,也就是并集的意思。QuerySet的模型必须是同样的,不能组合不同模型的查询集。

下面两种方式等同:

Model.objects.filter(x=1) | Model.objects.filter(y=2)


from django.db.models import Q
Model.objects.filter(Q(x=1) | Q(y=2))

四、get_next_by_FOO(**kwargs)get_previous_by_FOO(**kwargs)

这两个方法很少人知道。

Django为所有的DateField和DateTimeField类型的字段自动提供了这两个方法,只要它们没有设置null=True参数。

其中的FOO需要用具体的字段名称来代替。

这两个方法的作用是,返回时间分别在当前实例时间的前后的实例,如果没有找到,则弹出 DoesNotExist 异常。

假设有个模型叫做Question,它有个DateTimeField类型的字段名字叫做pub_date。假设模型的实例b的pub_date字段为中午12点,实例a的pub_date为早上8点,实例c的pub_date为下午6点,并且abc三者时间段中间没有其它的模型实例。那么:

  • b.get_next_by_pub_date()等于c
  • b.get_previous_by_pub_date()等于a

看下面的例子:

>>> from .models import Question
>>> questions = Question.objects.all()
>>> for q in questions:
...     print(q.pub_date)
...
2020-09-01 00:14:14.096933+00:00
2020-09-11 07:46:42+00:00
2020-09-01 22:00:00+00:00
>>> q = Question.objects.get(pk=3)
>>> q.pub_date
datetime.datetime(2020, 9, 1, 22, 0, tzinfo=<UTC>)
>>> next = q.get_next_by_pub_date()
>>> next
<Question: 你喜欢谁?>
>>> next.pub_date
datetime.datetime(2020, 9, 11, 7, 46, 42, tzinfo=<UTC>)
>>> pre = q.get_previous_by_pub_date()
>>> pre
<Question: What's new?>
>>> pre.pub_date
datetime.datetime(2020, 9, 1, 0, 14, 14, 96933, tzinfo=<UTC>)
>>> pre_of_pre = pre.get_previous_by_pub_date()
Traceback (most recent call last):
......
polls.models.Question.DoesNotExist: Question matching query does not exist.

 查询操作 不返回QuerySets的API 

评论总数: 14


点击登录后方可评论

l博主你好,queryset返回的字段太多,如何只返回指定的字段?



博主你好,我想问下为什么我用reverse会不生效?,还是原来的序列



>>> Entry.objects.order_by(Coalesce('summary', 'headline').desc()) Traceback (most recent call last): File "<console>", line 1, in <module> NameError: name 'Coalesce' is not defined 博主您好,为什么我这里的Console没定义?



博主,请问我如果想返回两张表的部分数据该怎么写呢? 比如说:评论功能,用户可以进行评论,那么我就应该设计一张用户表和一张评论表,用户评论后前端界面上应该显示的数据有用户的头像、信息、评论的内容等。但是貌似django的查询集api返回的都是单表的数据? 比如我在查询所有评论的时候all_comments = CourseComments.objects.filter(course=course_id) 但是这么写只能返回CourseComments这张表的数据吧? 如果我想加上user表的部分信息应该怎么做呢,user表和CourseComments表是已经做了外键关联的。



select_for_update()容易忽略,其实很重要



请问 Blog.objects.values('author', entries=Count('entry')) 是不是错了 Blog 表 和 author表没有关联啊? 做何理解?



它们通过Entry表建立了联系。有的API调用形式确实很绕。



你好,我在运行【Blog.objects.values('author', entries=Count('entry'))】查询语句的时候shell发生错误,错误如下: 【FieldError: Cannot resolve keyword 'author' into field. Choices are: entries, entry, id, name, tagline】 说选择中没有author的参数啊



请问QuerySet提交查询的迭代方法是先一次性拿到所有结果再逐个打印还是逐次访问数据库?



我理解是访问一次,不确定对不对



在首次迭代查询集时执行实际的数据库查询



博主写的真的好,比我在网上看的好多了0.0 谢谢博主



distinct()传入字段参数会报错,文中并没有给出详细的讲解。 annotate()的讲解并不直白明了,非常模糊。提示:annotate是注释的意思,Django为什么要用这么个词?



不直白明了的去百度一下再查查,我觉的已经无可挑剔了,只是需要认真细致的读上几遍。其实就明白了