Django【执行查询】(一)

官方Django3.2 文档https://docs.djangoproject.com/en/3.2/topics/db/queries/
本文大部分内容参考官方3.2版本文档撰写,仅供学习使用
官方PDF 下载链接https://media.readthedocs.org/pdf/django/3.2.x/django.pdf

Django 执行查询(一)

(本文中涉及的到的代码,建议复制粘贴的会以可折叠代码块展示,其他代码块建议手动练习。如果你对Models没有初步认识,欢迎去看看官方文档-Models或者其他文章。)

初步准备

将参考以下模型,它们构成了一个 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)
    number_of_comments = models.IntegerField()
    number_of_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):
        return self.headline

将修改保存到对象

使用模型类的关键字参数对其进行实例化,然后调用save()以将其保存到数据库中:

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

检索对象

如果你想查看Queryset相关API,可以点击这个链接
为了从数据库中检索对象,通过你的模型类上的Manager(管理器)构造一个QuerySet。过滤器根据给定的参数缩小查询结果的范围。在SQL术语中,一个QuerySet相当于一个SELECT语句,而过滤器是一个限制性子句,如WHERE或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."
</django.db.models.manager.manager>

【注意】

管理器(manger)只能通过模型类而不是 模型实例 来访问,以强制区分”表级”操作和”记录级”操作。

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

检索所有对象

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

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

all() 方法返回了数据库中所有对象的 QuerySet。

使用过滤器检索特定对象

all() 返回的 QuerySet 描述了数据库表中的所有对象。但是,通常您只需要选择完整对象集的一个子集。
要创建这样的子集,您需要优化初始 QuerySet,添加过滤条件。细化 QuerySet 的两种最常见的方法是:

filter(**kwargs)

返回一个新的 QuerySet,其中包含与给定查找参数匹配的对象。

exclude(**kwargs)

返回一个新的 QuerySet,其中包含与给定查找参数匹配的对象。

返回一个新的QuerySet,包含不符合给定查找参数的对象。
查询参数(上述函数定义中的**kwargs)应该采用下面字段查询中描述的格式。
例如,要包含获取 2006 年的博客条目(entries blog)的 QuerySet,像这样使用 filter():

Entry.objects.filter(pub_date__year=2006)
通过默认管理器类也一样:

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

链式过滤器

细化QuerySet 的结果本身还是一个 QuerySet,所以能串联多个细化过程。例子:

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

这个例子先获取包含数据库所有条目(entry)的 QuerySet,添加一个过滤器,然后排除一些,再进入另一个过滤器。最终的 QuerySet 包含标题以 “What” 开头的,发布日期介于 2005 年 1 月 30 日与今天之间的所有条目。

每个Queryset都是唯一的

每次精炼(细化)一个 QuerySet,你就会获得一个全新的 QuerySet,后者与前者毫无关联。每次精炼都会创建一个单独的、不同的 QuerySet,能被存储,使用和复用。

>>> 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 是独立的。第一个是基础 QuerySet,包含了所有标题以 “What” 开头的条目。第二个是第一个的子集,带有额外条件,排除了 pub_date 是今天和今天之后的所有记录。第三个是第一个的子集,带有额外条件,只筛选 pub_date 是今天或未来的所有记录。最初的 QuerySet (q1) 不受筛选操作影响。

QuerySet 是惰性的

QuerySet 是惰性的 —— 创建 QuerySet 并不会引发任何数据库活动。你可以将一整天的过滤器都堆积在一起,Django 只会在 QuerySet 被计算时执行查询操作。来瞄一眼这个例子:

>>> 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 的结果直到你 “要使用” 时才会从数据库中拿出。当你要用时,才通过数据库计算出 QuerySet。

用 get() 检索单个对象

filter() 总是返回一个 QuerySet,即便只有一个对象满足查询条件 —— 这种情况下,QuerySet 只包含了一个元素。

若你知道只会有一个对象满足查询条件,你可以在 Manager 上使用 get() 方法,它会直接返回这个对象:
>>> one_entry = Entry.objects.get(pk=1)

【注意】

使用切片 [0] 时的 get() 和 filter() 有点不同。如果没有满足查询条件的结果, get() 会抛出一个 DoesNotExist 异常。该异常是执行查询的模型类的一个属性 —— 所有,上述代码中,若没有哪个 Entry 对象的主键是 1,Django 会抛出 Entry.DoesNotExist。

类似了,Django 会在有不止一个记录满足 get() 查询条件时发出警告。这时,Django 会抛出 MultipleObjectsReturned,这同样也是模型类的一个属性。

所以说get()方法的使用中无论不存在或者存在多条都会报错,抛出的异常是可以被合理应用的

其它 QuerySet 方法

大多数情况下,你会在需要从数据库中检索对象时使用 all(), get(), filter() 和 exclude()。然而,这样远远不够;完整的各种 QuerySet 方法请参阅 QuerySet API reference

限制Queryset条目数

总结一下,可以切边,切边后的结果不能再排序or过滤。而且不能使用副索引

利用 Python 的数组切片语法将 QuerySet 切成指定长度。这等价于 SQL 的 LIMIT 和 OFFSET 子句。

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

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

这会返回第 6 至第 10 个对象 (OFFSET 5 LIMIT 5):

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

不支持负索引 (例如 Entry.objects.all()[-1])

一般情况下, QuerySet 的切片返回一个新的 QuerySet —— 其并未执行查询。一个特殊情况是使用了的 Python 切片语法的 “步长”。例如,这将会实际的执行查询命令,为了获取从前 10 个对象中,每隔一个抽取的对象组成的列表:

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

由于对 queryset 切片工作方式的模糊性,禁止对其进行进一步的排序或过滤。

要检索 单个 对象而不是一个列表时(例如 SELECT foo FROM bar LIMIT 1),请使用索引,而不是切片。例如,这会返回按标题字母排序后的第一个 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';< code><!--=-->

查询子句中指定的字段必须是模型的一个字段名。不过也有个例外,在 ForeignKey 中,你可以指定以 _id 为后缀的字段名。这种情况下,value 参数需要包含 foreign 模型的主键的原始值。例子:
(点击此连接 ForeignKey 官方详解(多对一关系))

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

若你传入了无效的关键字参数,查询函数会抛出 TypeError。

数据库 API 支持两套查询类型;完整参考文档位于 QuerySet API reference 字段查询参考。为了让你了解能干啥,以下是一些常见的查询:

exact

完全匹配。如果提供的比较值是 None,它将被解释为 SQL NULL (详见 isnull)。

一个 “exact” 匹配的例子:

>>> Entry.objects.get(headline__exact="Cat bites dog")
官方翻译:
若你没有提供查询类型 —— 也就说,若关键字参数未包含双下划线 —— 查询类型会被指定为 exact。
google翻译:
如果您不提供查找类型(即,如果您的关键字参数不包含双下划线),则假定查找类型是exact(精确)。
(下图为官方翻译,我觉得有点问题)

[En]

(the following picture is an official translation, which I think is a bit of a problem)

Django【执行查询】(一)

iexact

不区分大小写的完全匹配。如果提供的比较值是 None,它将被解释为 SQL NULL (详见 isnull)。

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

会匹配标题为 “Beatles Blog”, “beatles blog”, 甚至 “BeAtlES blOG” 的 Blog。

contains

区分大小写的包含测试。

举例:

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

SQL 等价于:

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

icontains

不区分大小写的包含测试。

[En]

Case-insensitive inclusion tests.

举例:

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

SQL 等价于:

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

startswith, endswith

区分大小写的开头为;区分大小写的结尾为.

以……开头和以……结尾的查找。当然也有大小写不敏感的版本,名为 istartswith 和 iendswith。

(官方文档中对 field 查询参考 没有再详细介绍,对更多字段查询感兴趣可以查看我google翻译的文章,点击这段话即可)

跨关系查询

Django 提供了一种强大而直观的方式来”追踪”查询中的关系,在幕后自动为你处理 SQL JOIN 关系。为了跨越关系,跨模型使用关联字段名,字段名由双下划线分割,直到拿到想要的字段。

本例检索出所有的 Entry 对象,其 Blog 的 name 为 ‘Beatles Blog’ :

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

此跨距可以是您想要的深度。

[En]

This span can be the depth you want.

它也可以反向工作。尽管可以对其进行定制,但默认情况下,您在查询中使用模型的小写名称来引用“反向”关系。

[En]

It can also work backwards. Although it can be customized, by default, you use the lowercase name of the model in the query to refer to the “reverse” relationship.

本例检索的所有 Blog 对象均拥有少一个 标题 含有 ‘Lennon’ 的条目:

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

Original: https://www.cnblogs.com/libai1024/p/Django_queries_1.html
Author: libai1024
Title: Django【执行查询】(一)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/499139/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球