进行查询

一旦你创建了你的data models,Django会自动为你提供一个数据库抽象API,允许你创建,检索,更新和删除对象。 本文档介绍了如何使用这个API。 有关所有各种模型查找选项的完整详细信息,请参阅data model reference

在本指南(以及参考资料)中,我们将参考以下模型,这些模型包含一个Weblog应用程序:

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)
    n_comments = models.IntegerField()
    n_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):
        return self.headline

创建对象

为了在Python对象中表示数据库表数据,Django使用直观的系统:模型类表示数据库表,该类的一个实例表示数据库表中的特定记录。

要创建一个对象,使用模型类的关键字参数实例化它,然后调用save()将其保存到数据库中。

Assuming models live in a file mysite/blog/models.py, here’s an example:

>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()

这将在幕后执行一个INSERT SQL语句。 直到你明确地调用了save()之后,Django才会触发数据库。

save()方法没有返回值。

也可以看看

save() takes a number of advanced options not described here. 有关完整的详细信息,请参阅save()的文档。

要在一个步骤中创建和保存对象,请使用create()方法。

保存对对象的更改

要保存对数据库中已有对象的更改,请使用save()

给定一个已经保存到数据库的Blog实例b5,这个例子改变它的名字并更新数据库中的记录:

>>> b5.name = 'New name'
>>> b5.save()

这将在幕后执行一个UPDATE SQL语句。 直到你明确地调用了save()之后,Django才会触发数据库。

保存ForeignKeyManyToManyField字段

更新一个ForeignKey字段的工作方式与保存普通字段的方式完全相同 - 只需将正确类型的对象分配给正在讨论的字段即可。 这个例子更新了Entry实例entryblog属性,假设EntryBlog已经保存到数据库(所以我们可以在下面检索它们):

>>> from blog.models import Blog, Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()

更新ManyToManyField的工作方式稍有不同 - 使用字段上的add()方法将记录添加到关系中。 本例将Author实例joe添加到entry对象中:

>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)

要一次将多个记录添加到ManyToManyField中,请在调用add()时包含多个参数,如下所示:

>>> john = Author.objects.create(name="John")
>>> paul = Author.objects.create(name="Paul")
>>> george = Author.objects.create(name="George")
>>> ringo = Author.objects.create(name="Ringo")
>>> entry.authors.add(john, paul, george, ringo)

如果您尝试分配或添加错误类型的对象,Django会投诉。

检索对象

要从您的数据库中检索对象,请通过模型类上的Manager构造一个QuerySet

一个QuerySet表示数据库中的对象集合。 它可以有零个,一个或多个过滤器 筛选器根据给定的参数缩小查询结果的范围。 In SQL terms, a QuerySet equates to a SELECT statement, and a filter is a limiting clause such as WHERE or LIMIT.

你通过使用模型的Manager来获得一个QuerySet 每个模型至少有一个Manager,默认情况下称为objects 通过模型类直接访问它,如下所示:

>>> Blog.objects
<django.db.models.manager.Manager object at ...>
>>> b = Blog(name='Foo', tagline='Bar')
>>> b.objects
Traceback:
    ...
AttributeError: "Manager isn't accessible via Blog instances."

注意

Managers are accessible only via model classes, rather than from model instances, to enforce a separation between “table-level” operations and “record-level” operations.

Manager是模型的QuerySets的主要来源。 例如,Blog.objects.all()返回包含数据库中所有Blog对象的QuerySet

检索所有对象

从表中检索对象最简单的方法是获取所有的对象。 为此,请使用Manager上的all()方法:

>>> all_entries = Entry.objects.all()

The all() method returns a QuerySet of all the objects in the database.

使用过滤器检索特定对象

all()返回的QuerySet描述数据库表中的所有对象。 通常情况下,您只需要选择整个对象集合的一个子集。

要创建这样一个子集,可以优化初始的QuerySet,添加过滤条件。 提炼QuerySet的两种最常见的方法是:

过滤(** kwargs)
返回包含匹配给定查找参数的对象的新的QuerySet
排除(** kwargs)
返回一个新的QuerySet,其中包含not匹配给定查找参数的对象。

上述函数定义中的查找参数(**kwargs)应该采用下面字段查找中描述的格式。

例如,要获得2006年的博客条目的QuerySet,请使用filter(),如下所示:

Entry.objects.filter(pub_date__year=2006)

使用默认管理器类,它与以下内容相同:

Entry.objects.all().filter(pub_date__year=2006)

链接过滤器

The result of refining a QuerySet is itself a QuerySet, so it’s possible to chain refinements together. 例如:

>>> Entry.objects.filter(
...     headline__startswith='What'
... ).exclude(
...     pub_date__gte=datetime.date.today()
... ).filter(
...     pub_date__gte=datetime.date(2005, 1, 30)
... )

这需要数据库中所有条目的初始QuerySet,添加一个过滤器,然后是一个排除,然后是另一个过滤器。 The final result is a QuerySet containing all entries with a headline that starts with “What”, that were published between January 30, 2005, and the current day.

过滤的QuerySet是唯一的

Each time you refine a QuerySet, you get a brand-new QuerySet that is in no way bound to the previous QuerySet. Each refinement creates a separate and distinct QuerySet that can be stored, used and reused.

例:

>>> q1 = Entry.objects.filter(headline__startswith="What")
>>> q2 = q1.exclude(pub_date__gte=datetime.date.today())
>>> q3 = q1.filter(pub_date__gte=datetime.date.today())

这三个QuerySets是分开的。 第一个是包含所有包含以“What”开头的标题的条目的基础QuerySet The second is a subset of the first, with an additional criteria that excludes records whose pub_date is today or in the future. The third is a subset of the first, with an additional criteria that selects only the records whose pub_date is today or in the future. 最初的QuerySetq1)不受细化过程的影响。

QuerySets are lazy

QuerySets是懒惰的 - 创建QuerySet的行为不涉及任何数据库活动。 You can stack filters together all day long, and Django won’t actually run the query until the QuerySet is evaluated. 看看这个例子:

>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.date.today())
>>> q = q.exclude(body_text__icontains="food")
>>> print(q)

虽然这看起来像三个数据库命中,但实际上它只在最后一行(print(q))命中数据库一次。 一般来说,在你询问它们之前,不会从数据库中取出QuerySet的结果。 When you do, the QuerySet is evaluated by accessing the database. 有关确切何时进行评估的更多详细信息,请参阅When QuerySets are evaluated

get() 检索单个对象

filter() will always give you a QuerySet, even if only a single object matches the query - in this case, it will be a QuerySet containing a single element.

如果您知道只有一个对象与您的查询匹配,则可以在Manager上使用get()方法,该方法直接返回对象:

>>> one_entry = Entry.objects.get(pk=1)

您可以像使用filter()一样在get()中使用任何查询表达式 - 同样请参阅下面的Field lookups

请注意,使用get()和使用filter()[0]切片有区别。 If there are no results that match the query, get() will raise a DoesNotExist exception. 这个异常是正在执行查询的模型类的一个属性 - 所以在上面的代码中,如果没有主键为1的Entry对象,Django将引发Entry.DoesNotExist

同样,如果多个项目匹配get()查询,Django将会投诉。 在这种情况下,它将引发MultipleObjectsReturned,这又是模型类本身的一个属性。

其他QuerySet方法

Most of the time you’ll use all(), get(), filter() and exclude() when you need to look up objects from the database. 但是,这还远远不够。请参阅QuerySet API Reference以获取所有各种QuerySet方法的完整列表。

限制QuerySet s

使用Python的数组切片语法的子集将您的QuerySet限制为一定数量的结果。 这相当于SQL的LIMITOFFSET子句。

例如,这将返回前5个对象(LIMIT 5):

>>> Entry.objects.all()[:5]

这将返回第六到第十个对象(OFFSET 5 LIMIT 5

>>> Entry.objects.all()[5:10]

否定索引(即Entry.objects.all()[-1])不受支持。

通常,切分QuerySet会返回一个新的QuerySet - 它不计算查询。 如果使用Python切片语法的“step”参数,则是一个例外。 例如,这实际上会执行查询,以便返回第一个10的每个第二个对象的列表:

>>> Entry.objects.all()[:10:2]

禁止进一步筛选或排序切片查询集,因为这可能工作的含糊不清的性质。

要检索单个对象而不是列表(例如, SELECT foo FROM bar LIMIT 1), use a simple index instead of a slice. 例如,这将按照标题按字母顺序排序后返回数据库中的第一个Entry

>>> Entry.objects.order_by('headline')[0]

这大致相当于:

>>> Entry.objects.order_by('headline')[0:1].get()

但是,请注意,如果没有对象符合给定的条件,则第一个会提高IndexError,而另一个将提高DoesNotExist 有关更多详细信息,请参阅get()

字段查找

字段查找是如何指定SQL WHERE子句的内容。 它们被指定为QuerySet方法filter()exclude()get()

基本查找关键字参数的形式为field__lookuptype=value (这是一个双下划线)。 例如:

>>> Entry.objects.filter(pub_date__lte='2006-01-01')

将(粗略)转换为以下SQL:

SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';

这怎么可能

Python有能力定义接受任意名称和值的参数,这些参数的名称和值在运行时被评估。 有关更多信息,请参阅官方Python教程中的Keyword Arguments

在查找中指定的字段必须是模型字段的名称。 但有一个例外,如果是ForeignKey,您可以指定后缀_id的字段名称。 在这种情况下,value参数应该包含外部模型主键的原始值。 例如:

>>> Entry.objects.filter(blog_id=4)

If you pass an invalid keyword argument, a lookup function will raise TypeError.

数据库API支持大约二十几种查找类型;可以在field lookup reference中找到完整的参考。 为了让您了解可用的功能,以下是您可能会使用的一些更常见的查找:

精确

一个“确切”的匹配。 例如:

>>> Entry.objects.get(headline__exact="Cat bites dog")

将生成SQL沿着这些线路:

SELECT ... WHERE headline = 'Cat bites dog';

如果不提供查找类型 - 也就是说,如果您的关键字参数不包含双下划线 - 则查找类型被假定为exact

例如,以下两条语句是等价的:

>>> Blog.objects.get(id__exact=14)  # Explicit form
>>> Blog.objects.get(id=14)         # __exact is implied

这是为了方便,因为exact查找是常见的情况。

iexact

不区分大小写的匹配。 所以,查询:

>>> Blog.objects.get(name__iexact="beatles blog")

Would match a Blog titled "Beatles Blog", "beatles blog", or even "BeAtlES blOG".

包含

区分大小写的容器测试。 例如:

Entry.objects.get(headline__contains='Lennon')

粗略地转换为这个SQL:

SELECT ... WHERE headline LIKE '%Lennon%';

Note this will match the headline 'Today Lennon honored' but not 'today lennon honored'.

还有一个不区分大小写的版本,icontains

startswithendswith
开始 - 与搜索结束,分别。 还有一些不区分大小写的版本,称为istartswithiendswith

再次,这只是划痕表面。 可以在field lookup reference中找到完整的参考。

跨越关系的查找

Django提供了一种强大而直观的方式来在查找中“追踪”关系,在幕后自动为您处理SQL JOIN 要跨越关系,只需使用跨模型的相关字段的字段名称,用双下划线分隔,直到您到达所需的字段。

This example retrieves all Entry objects with a Blog whose name is 'Beatles Blog':

>>> Entry.objects.filter(blog__name='Beatles Blog')

这个跨越可以像你想的那么深。

它也可以倒退。 要引用“反向”关系,只需使用模型的小写名称即可。

This example retrieves all Blog objects which have at least one Entry whose headline contains 'Lennon':

>>> Blog.objects.filter(entry__headline__contains='Lennon')

如果你跨多个关系进行过滤,并且其中一个中间模型没有满足过滤条件的值,那么Django将把它看作是空的(所有的值都是NULL),但是有效,对象在那里。 所有这一切都意味着不会出现任何错误。 例如,在这个过滤器中:

Blog.objects.filter(entry__authors__name='Lennon')

(if there was a related Author model), if there was no author associated with an entry, it would be treated as if there was also no name attached, rather than raising an error because of the missing author. 通常这正是你想要发生的事情。 唯一可能会引起混淆的情况是,如果您正在使用isnull 从而:

Blog.objects.filter(entry__authors__name__isnull=True)

will return Blog objects that have an empty name on the author and also those which have an empty author on the entry. 如果你不想要那些后面的对象,你可以这样写:

Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)

跨越多值关系

在根据ManyToManyField或者ForeignKey过滤对象时,您可能会感兴趣的是两种不同类型的过滤器。 考虑Blog / Entry关系(Blog to Entry是一对多关系)。 We might be interested in finding blogs that have an entry which has both “Lennon” in the headline and was published in 2008. 或者我们可能想要找到标题为“Lennon”的博客以及2008年发布的博客。 由于有多个条目与一个Blog关联,因此在某些情况下,这两个查询都是可能的并且合理。

一个ManyToManyField出现同样的情况。 例如,如果一个Entry有一个名为tagsManyToManyField,我们可能希望找到链接到“music 和“band”,或者我们可能需要一个包含名称为“music”“public” T10>。

为了处理这两种情况,Django有一个处理filter()调用的一致方法。 同时应用单个filter()调用中的所有内容,以过滤出符合所有这些要求的项目。 连续的filter()调用进一步限制了对象集合,但是对于多值关系,它们适用于链接到主模型的任何对象,而不一定是早期filter()调用。

这可能听起来有些混乱,希望有个例子可以澄清。 要选择标题中包含“Lennon”和2008年发布的条目(满足这两个条件的条目)的所有博客,我们会写:

Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)

要选择包含“Lennon”在标题以及 2008年发布的条目中的所有博客,我们将编写:

Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)

Suppose there is only one blog that had both entries containing “Lennon” and entries from 2008, but that none of the entries from 2008 contained “Lennon”. 第一个查询不会返回任何博客,但第二个查询将返回一个博客。

在第二个示例中,第一个过滤器将查询集限制为链接到标题中带有“Lennon”的条目的所有博客。 第二个过滤器将这组博客进一步限制为那些也链接到2008年发布的条目的博客。 由第二过滤器选择的条目可能与第一过滤器中的条目相同或不同。 我们正在使用每个过滤器语句过滤Blog项目,而不是Entry项目。

注意

如上所述,对于跨越多值关系的查询,filter()的行为没有针对exclude()等同执行。 相反,单个exclude()调用中的条件不一定引用相同的项目。

例如,以下查询将在2008年发布的标题为的条目中排除同时包含条目和“Lennon”

Blog.objects.exclude(
    entry__headline__contains='Lennon',
    entry__pub_date__year=2008,
)

但是,与使用filter()时的行为不同,这不会基于满足这两个条件的条目来限制博客。 为了做到这一点,即选择所有不包含用“Lennon”发布的条目的博客,您需要进行两项查询:

Blog.objects.exclude(
    entry__in=Entry.objects.filter(
        headline__contains='Lennon',
        pub_date__year=2008,
    ),
)

过滤器可以引用模型上的字段

在迄今给出的例子中,我们已经构建了一个过滤器,用于比较模型字段的值和一个常量。 但是,如果你想比较模型字段的值与同一模型中的另一个字段呢?

Django提供了F expressions来允许这样的比较。 Instances of F() act as a reference to a model field within a query. 然后可以在查询过滤器中使用这些引用来比较同一模型实例上两个不同字段的值。

例如,要查找比pingbacks多的注释的所有博客条目列表,我们构造一个F()对象来引用pingback计数,并使用该对象的F()

>>> from django.db.models import F
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))

Django supports the use of addition, subtraction, multiplication, division, modulo, and power arithmetic with F() objects, both with constants and with other F() objects. 要查找所有超过两次与pingbacks一样多的评论的博客条目,我们修改查询:

>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)

要查找条目评级小于pingback计数和评论计数总和的所有条目,我们将发出查询:

>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))

您还可以使用双下划线表示法来跨越F()对象中的关系。 具有双下划线的F()对象将引入访问相关对象所需的任何连接。 例如,要检索作者姓名与博客名称相同的所有条目,我们可以发出查询:

>>> Entry.objects.filter(authors__name=F('blog__name'))

对于日期和日期/时间字段,可以添加或减去一个timedelta对象。 以下内容将返回发布后超过3天修改的所有条目:

>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))

The F() objects support bitwise operations by .bitand(), .bitor(), .bitrightshift(), and .bitleftshift(). 例如:

>>> F('somefield').bitand(16)
在Django 1.11中更改:

支持.bitrightshift().bitleftshift()

pk查找快捷方式

为了方便起见,Django提供了一个pk查找快捷方式,它代表“主键”。

在示例Blog模型中,主键是id字段,所以这三个语句是等价的:

>>> Blog.objects.get(id__exact=14) # Explicit form
>>> Blog.objects.get(id=14) # __exact is implied
>>> Blog.objects.get(pk=14) # pk implies id__exact

使用pk并不限于__exact查询 - 任何查询词都可以与pk组合来执行查询一个模型:

# Get blogs entries with id 1, 4 and 7
>>> Blog.objects.filter(pk__in=[1,4,7])

# Get all blog entries with id > 14
>>> Blog.objects.filter(pk__gt=14)

pk lookups also work across joins. 例如,这三个语句是等价的:

>>> Entry.objects.filter(blog__id__exact=3) # Explicit form
>>> Entry.objects.filter(blog__id=3)        # __exact is implied
>>> Entry.objects.filter(blog__pk=3)        # __pk implies __id__exact

LIKE语句中转义百分号和下划线

The field lookups that equate to LIKE SQL statements (iexact, contains, icontains, startswith, istartswith, endswith and iendswith) will automatically escape the two special characters used in LIKE statements – the percent sign and the underscore. (在LIKE语句中,百分号表示多字符通配符,下划线表示单字符通配符。)

这意味着事情应该直观地工作,所以抽象不会泄漏。 例如,要检索包含百分号的所有条目,只需使用百分号作为其他任何字符:

>>> Entry.objects.filter(headline__contains='%')

Django负责为你引用;生成的SQL将如下所示:

SELECT ... WHERE headline LIKE '%\%%';

下划线也一样。 透明地处理您的百分比符号和下划线。

缓存和QuerySet s

每个QuerySet包含一个缓存,以最大限度地减少数据库访问。 了解它的工作原理将允许您编写最高效的代码。

在新创建的QuerySet中,缓存是空的。 The first time a QuerySet is evaluated – and, hence, a database query happens – Django saves the query results in the QuerySet’s cache and returns the results that have been explicitly requested (e.g., the next element, if the QuerySet is being iterated over). 随后对QuerySet的评估重用了缓存的结果。

记住这个缓存行为,因为如果你没有正确使用你的QuerySet,它可能会咬你。 例如,以下将创建两个QuerySet,对它们进行评估并将其丢弃:

>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])

这意味着相同的数据库查询将执行两次,有效地加倍你的数据库负载。 另外,这两个列表可能不包含相同的数据库记录,因为Entry可能已经在两个请求之间的分秒中被添加或删除。

为了避免这个问题,只需保存QuerySet并重用:

>>> queryset = Entry.objects.all()
>>> print([p.headline for p in queryset]) # Evaluate the query set.
>>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.

QuerySet没有被缓存时

查询集并不总是缓存结果。 当仅评估queryset的部分时,将检查缓存,但是如果未填充,则后续查询返回的项目不会被缓存。 Specifically, this means that limiting the queryset using an array slice or an index will not populate the cache.

例如,重复获取某个查询集对象中的某个索引将每次查询数据库:

>>> queryset = Entry.objects.all()
>>> print(queryset[5]) # Queries the database
>>> print(queryset[5]) # Queries the database again

但是,如果整个查询集已经被评估,缓存将被检查:

>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print(queryset[5]) # Uses cache
>>> print(queryset[5]) # Uses cache

以下是将导致整个查询集被评估并因此填充缓存的其他操作的一些示例:

>>> [entry for entry in queryset]
>>> bool(queryset)
>>> entry in queryset
>>> list(queryset)

注意

简单地打印查询集不会填充缓存。 这是因为对__repr__()的调用只返回整个查询集的一部分。

使用Q对象进行复杂查找

关键字参数查询 - 在filter()等中 - 是“AND”编辑在一起的。 如果您需要执行更复杂的查询(例如,使用OR语句的查询),您可以使用Q objects

A Q object (django.db.models.Q) is an object used to encapsulate a collection of keyword arguments. 这些关键字参数在上面的“字段查找”中指定。

例如,这个Q对象封装了一个LIKE查询:

from django.db.models import Q
Q(question__startswith='What')

Q objects can be combined using the & and | operators. 当两个Q对象使用一个运算符时,它将产生一个新的Q对象。

例如,这个语句产生一个单一的Q对象,它表示两个"question__startswith"查询的“OR”:

Q(question__startswith='Who') | Q(question__startswith='What')

这相当于下面的SQL WHERE子句:

WHERE question LIKE 'Who%' OR question LIKE 'What%'

您可以通过将Q对象与&|运算符组合来构造任意复杂的语句,并使用括号分组。 Also, Q objects can be negated using the ~ operator, allowing for combined lookups that combine both a normal query and a negated (NOT) query:

Q(question__startswith='Who') | ~Q(pub_date__year=2005)

Each lookup function that takes keyword-arguments (e.g. filter(), exclude(), get()) can also be passed one or more Q objects as positional (not-named) arguments. 如果您为查找函数提供多个Q对象参数,则参数将被“AND”编辑在一起。 例如:

Poll.objects.get(
    Q(question__startswith='Who'),
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

...粗略地翻译成SQL:

SELECT * from polls WHERE question LIKE 'Who%'
    AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')

查找函数可以混合使用Q对象和关键字参数。 提供给查询函数的所有参数(不管是关键字参数还是Q对象)都是“与”并列的。 但是,如果提供了Q对象,则必须先于任何关键字参数的定义。 例如:

Poll.objects.get(
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    question__startswith='Who',
)

...将是一个有效的查询,相当于前面的例子;但:

# INVALID QUERY
Poll.objects.get(
    question__startswith='Who',
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

...不会有效。

也可以看看

Django单元测试中的OR查找示例显示了Q的一些可能用法。

比较对象

要比较两个模型实例,只需使用标准的Python比较运算符double等号:== 在幕后,比较了两个模型的主要关键值。

使用上面的Entry示例,以下两条语句是等价的:

>>> some_entry == other_entry
>>> some_entry.id == other_entry.id

如果模型的主键不叫id,没问题。 不管它叫什么,比较总是使用主键。 例如,如果一个模型的主键字段被称为name,这两个语句是等价的:

>>> some_obj == other_obj
>>> some_obj.name == other_obj.name

删除对象

删除方法很方便,名为delete() 该方法立即删除对象,并返回删除的对象数量和每个对象类型的删除次数的字典。 例:

>>> e.delete()
(1, {'weblog.Entry': 1})

您也可以批量删除对象。 每个QuerySet都有一个delete()方法,该方法删除QuerySet的所有成员。

例如,这将删除2005年的pub_date年份的所有Entry对象:

>>> Entry.objects.filter(pub_date__year=2005).delete()
(5, {'webapp.Entry': 5})

请记住,只要有可能,这将会纯粹在SQL中执行,因此单个对象实例的delete()方法在进程中不一定会被调用。 If you’ve provided a custom delete() method on a model class and want to ensure that it is called, you will need to “manually” delete instances of that model (e.g., by iterating over a QuerySet and calling delete() on each object individually) rather than using the bulk delete() method of a QuerySet.

When Django deletes an object, by default it emulates the behavior of the SQL constraint ON DELETE CASCADE – in other words, any objects which had foreign keys pointing at the object to be deleted will be deleted along with it. 例如:

b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()

这个级联行为可以通过ForeignKeyon_delete参数进行定制。

请注意,delete()Manager本身没有公开的唯一的QuerySet方法。 This is a safety mechanism to prevent you from accidentally requesting Entry.objects.delete(), and deleting all the entries. If you do want to delete all the objects, then you have to explicitly request a complete query set:

Entry.objects.all().delete()

复制模型实例

虽然没有用于复制模型实例的内置方法,但是可以轻松创建复制了所有字段值的新实例。 在最简单的情况下,您可以将pk设置为None 使用我们的博客示例:

blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1

blog.pk = None
blog.save() # blog.pk == 2

如果使用继承,情况会变得更加复杂。 考虑一下Blog的子类:

class ThemeBlog(Blog):
    theme = models.CharField(max_length=200)

django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme='python')
django_blog.save() # django_blog.pk == 3

由于继承是如何工作的,你必须将pkid设置为None:

django_blog.pk = None
django_blog.id = None
django_blog.save() # django_blog.pk == 4

此过程不复制不属于模型数据库表的一部分的关系。 例如,Entry具有ManyToManyFieldAuthor 复制一个条目之后,您必须设置新条目的多对多关系:

entry = Entry.objects.all()[0] # some previous entry
old_authors = entry.authors.all()
entry.pk = None
entry.save()
entry.authors.set(old_authors)

对于OneToOneField,您必须复制相关对象并将其分配给新对象的字段,以避免违反一对一的唯一约束。 例如,假设entry已经被重复如上:

detail = EntryDetail.objects.all()[0]
detail.pk = None
detail.entry = entry
detail.save()

一次更新多个对象

有时你想为QuerySet中的所有对象设置一个字段为特定的值。 你可以用update()方法做到这一点。 例如:

# Update all the headlines with pub_date in 2007.
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')

您只能使用此方法设置非关系字段和ForeignKey字段。 要更新非关系字段,请将新值作为常量提供。 要更新ForeignKey字段,请将新值设置为要指向的新模型实例。 例如:

>>> b = Blog.objects.get(pk=1)

# Change every Entry so that it belongs to this Blog.
>>> Entry.objects.all().update(blog=b)

立即应用update()方法,并返回查询匹配的行数(如果某些行已经有新值,可能不等于更新的行数)。 QuerySet进行更新的唯一限制是它只能访问一个数据库表:模型的主表。 您可以根据相关字段进行过滤,但只能更新模型主表中的列。 例:

>>> b = Blog.objects.get(pk=1)

# Update all the headlines belonging to this Blog.
>>> Entry.objects.select_related().filter(blog=b).update(headline='Everything is the same')

请注意,update()方法直接转换为SQL语句。 这是直接更新的批量操作。 It doesn’t run any save() methods on your models, or emit the pre_save or post_save signals (which are a consequence of calling save()), or honor the auto_now field option. 如果要将每个项目保存在QuerySet中,并确保在每个实例上调用了save()方法,则不需要任何特殊的函数来处理。 只需循环,并调用save()

for item in my_queryset:
    item.save()

调用更新也可以使用F expressions来根据模型中另一个字段的值更新一个字段。 这对于根据当前值递增计数器特别有用。 例如,要增加博客中每个条目的pingback计数:

>>> Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)

但是,与过滤器和排除子句中的F()对象不同,在更新中使用F()对象时不能引入连接 - 只能引用本地到正在更新的模型。 如果试图引入一个包含F()对象的连接,将引发一个FieldError

# This will raise a FieldError
>>> Entry.objects.update(headline=F('blog__name'))

回落到原始SQL

如果您发现自己需要编写一个对Django的数据库映射器来说太复杂的SQL查询来处理,那么可以不用手工编写SQL语句。 Django有几个编写原始SQL查询的选项;请参阅Performing raw SQL queries

最后,需要注意的是,Django数据库层仅仅是数据库的一个接口。 您可以通过其他工具,编程语言或数据库框架访问您的数据库; Django并没有针对你的数据库。