崇礼天气,探索djangorm的局限性-安博电竞网站-安博电竞app下载-安博电竞

国际新闻 140℃ 0

​我最近在2019年欧洲Django大会(https://2019.djangocon.eu/ )上宣布了一场关于Django ORM的讲演。在这次讲演中,我展现了运用Django ORM进行杂乱查询时能够运用的各种技能。这篇文章将部分总结这次讲演,但我也会扩展和增加我无法在30分钟内完结的额定的内容。

首要,ORM代表目标联系映射,是一个协助你处理数据库的东西。Django ORM供给了一个Python接口,用于处理数据库中的数据。它对你有两大优点: 它经过运用模型界说和搬迁来协助你设置和维护数据库结构,并经过管理器和查询集协助你编写针对数据库的查询。

Django ORM没有敞开一个接口来让你编写自界说SQL。该接口只关董进宇的教育的本相注你界说的模型。这使得运用ORM十分简略,但也使得运用ORM编写某些查询更困难——乃至不或许。

示例模型

在本文中,我将在大多数比如中运用下面界说的模型:


自界说管理器和查询集

咱们在Kolonial.no上许多运用的东西是为咱们的模型定制的Manager(管理器)和QuerySet(查询集)。在这儿,你能够坚持与你的模型相关的可重用逻辑。例如,咱们能够在订单QuerySet中增加一个办法,该办法给出一个未发货订单的列表:

相似地,咱们能够创立一个自界说管理器,例如运用一个协助器办法来简化新订单的创立:

为了设置order(订单)模型的默许管理器,咱们设置了objects特点:


检查QuerySet

另一个需求知道的有用技巧是怎么检查一个QuerySet。假定你想知道为什么一个特定的QuerySet不能精确地回来你所期望的成果。那么,翻开一个shell并实践检查数据库中正在运转的查询是十分有用的。这儿我特别想着重两件事: 怎么查崇礼气候,探究djangorm的局限性-安博电竞网站-安博电竞app下载-安博电竞看某个QuerySet生成的SQL查询,以及怎么在数据库中运转一个EXPLAIN查询。

你还能够拜访Django中的数据库衔接包装器,并检查在该衔接上运转的终究查询:

这将给出履行的SQL句子的列表和每个查询的运转时刻。

防止额定的查询

当运用Django ORM时,很简略呈现视图生成过多查询的状况。假如你有一领结婚证需求什么个相关的模型,并对QuerySet中的每个实例进行拜访,则默许行为是一次获取一个相关的模型。


为了防止这种状况,你能够运用select_related和prefetch_related。它们有着十分相似的姓名和行为。榜首个用于获取数据库中的目标,这些目标每一个都和一行相关联,并生成一个JOIN查询,其间一切相关目标都在一个SQL查询中获取。当你的数据库中每一行具有多个相关目标时,你能够运用第二种办法。它将首要忽然好想你获取原始QuerySet的一切目标,而不是创立一个JOIN查询。然后,它将运转第二个查询来获取一切相关目标,然后在Python中而不是数据库中来衔接它们。因而,经过在咱们的Orders QuerySet中增加以下两行代码,咱们终究会得到两个查询:

可是请注意,你不应该盲目地优化。有时运转两个或多个查询比运转一个大安意如型查询更快。所以,在研讨功用问题时,请记住这一点。

防止竞赛条件

假如你正在处理数据库中的目标,这些目标能够经过多个恳求并发地进行修正,那么你需求确保这些更改是依照正确的次序运用的。这或许很重要,例如,假如咱们在产品模型上坚持库存数量,咱们期望确保该数量正确地递加和递减。对此的一种处理方案是,当咱们从数据库中获取目标时,对数据库中的行进行确定。这样,咱们就能够确保不答应其他恳求获取和修正相同的行。

就功用而言,这是一个适当深重的处理方案,在业务完结之前,不答应任何其他数据库衔接拜访这一行。在对你的数据进行建模时,请记住这一点。或许你能够经过对数据进行不同的建模来提早防止这个问题?

子查询

当你需求从另一个表(有时乃至是同一个表)获取一些数据,但在Django模型中又没有任何直接相关字段时,子查询十分有用。在这种状况下,Django现在不答应履行衔接。另一个用例是当要获吴晓波取或查找的数据量太大,履行一般衔接的速度太慢的时分。

我的榜首个示例向你展现了怎么将来自一行的单个值注释到一个QuerySet上。在这个比如中,我用客户下终究一笔订单的时刻来标示每个客户目标:

这儿两个风趣的部分是Subquery和OuterRef类。榜首个是一个包装器,它承受一个一般的QuerySet并将其作为子查询嵌入到另一个QuerySet中。OuterRef类用于引证嵌入子查询的Q齐天大圣孙悟空uerySet中的字段。在本例中,咱们运用它进行挑选,以便只取得归于当时行客户的订单。然后按日期排序,只挑选日期列,然后只回来榜首个成果。

除了从另一个QuerySet中挑选一个特定的值,咱们还能够运用Exists类检查另一个目标是否存在:

假如你对挑选成果不感兴趣,而仅仅想过滤,那么Django崇礼气候,探究djangorm的局限性-安博电竞网站-安博电竞app下载-安博电竞现在不支撑这种办法。这很不幸,因为这会导致查询速度变慢。走运的是,现在有一个敞开的崇礼气候,探究djangorm的局限性-安博电竞网站-安博电竞app下载-安博电竞推送恳求(https://github.com/django/django/pull/8119 )来处理这个问题,所以在Django的未来版别中,很或许会支嘬奶持这个功用。

上面的两个比如仅仅从数据库中的另一个目标中挑选一个值,可是咱们也能够运用聚合的成果运转更杂乱的查询。下面是怎么聚合子查询中匹配行的总和的比如:

这儿咱们要做的榜首件事是过滤出咱们感兴趣的一组行。然后,咱们运用values_list只挑选年和月的成果。当咱们在此之后运用聚合函数进行注释时,查询成果将依据所选的行进行分组,这些行关于匹配挑选器的任何行来说都是专一的。然后,咱们只挑选聚合值并将其注释到外部QuerySet上。

在本例中,咱们能够运用默许可用的Django根本类型生成这个查询,可是假如咱们想要聚合一些行,而这些行中没有专一的东西能够进行分组,该怎么办呢?咱们不能在子查询中运用aggregate,因为它会当即运转数据库查询。相反,咱们需求做的是在ORM周围运用一些技巧。假定咱们有这张订单表,并想核算一切周六和周日的总销售额:

咱们没有任何共同的东西能够用来分组,可是咱们能够测验删去values_list调用。不幸的是,这并没有带给咱们主播米娜想要的成果:

这不起作用的原因是,每逢咱们运用带注释的Django向QuerySet增加一个聚合时,都会增加一个group by句子,在本例Order.pk中,默许状况下,这是QuerySet模型的主键。成果是咱们只得到榜首个订单的和。相反,咱们有必要运用一个不承继自Aggregate类的数据库函数。在Django中,SUM SQ刘昱妤lexieL函数只能作为一个聚合子类运用,可是咱们能够相对简略地经过直接运用Func类来处理这个问题:

尽管这不是特别漂海滨风景图片亮,但它的确给出了咱们想要的成果。尽管我并不总是主张运用这个办法,可是知道它是一个可用选项是很有用的。

自界说束缚和崇礼气候,探究djangorm的局限性-安博电竞网站-安博电竞app下载-安博电竞索引

D崇礼气候,探究djangorm的局限性-安博电竞网站-安博电竞app下载-安博电竞jango在很长一段时刻内都支撑仅有性束缚,可是只要当你期望字段的组合在表中的一切行中都是仅有的时分才需求运用它:

咱们还能够挑选向表中的某些列增加额定的索引,但相同只针对整个表。从Django2.2开端,咱们能够更好地操控怎么创立仅有性束缚和自界说索引。咱们现在能够指定一个仅有性束缚只检查表中一切行的一个子集:

在本例中,咱们将每个用户束缚为只要一个未发货订单。尽管这或许是一个相对简略的比如,可是条件仅有性束缚给了咱们很大的灵活性。假定咱们有一个数据库表,其间咱们期望只答应一行具有NULL值。因为在SQL中NULL是不等于N张狂的麦咭ULL的,所以每一崇礼气候,探究djangorm的局限性-安博电竞网站-安博电竞app下载-安博电竞行都被认为是专一的,咱们不能运用一个一般的unique=True或unique_together来束缚这一点。可是,咱们图拉丁吧能够假如增加一个束缚,在字段为NULL的状况下检查它是否是仅有的。

咱们还能够创立自界说检查束缚。这相似于Django中的字段验证器,可是它们是由数据库履行的。这能够维护咱们不受Python代码中的bug的影响,该检查乃至能够在bul崇礼气候,探究djangorm的局限性-安博电竞网站-安博电竞app下载-安博电竞k_create和相似的平台上运转,假如你从另一个非django项目拜访相同的数据库时,该检查也会运转。例如,咱们能够创立一个检查束缚来验证给定的月份数字是否有用:

相似地,咱们能够创立自界说的部分索引,它只掩盖表的一部分。假如只查询表的一个子集,或许表的很大一部分包括空NULL值时,这将十分有用。在咱们的示例模型中,在预备发货时,咱们很或许会频频地拜访未发货的订单。下面是怎么创立只包括未发货订单的索引的示例。这将有助于加速咱们最常用的数据库查询:

窗口函数

在Django2.0中,运用Dj六和彩图库angoORM运转窗口函数进行查询得到了支撑。这将在查询中生成一个OVER句子来检查行分区。这是很有用的,例如,假如咱们想查找某个客户的上一个订单,检查该客户上一次下订单的时刻,或许核算来自该客户的订单总和。下面是一个示例,演示怎么运用来自同一客户的上一个订单的id来注释一个订单的QuerySet:

咱们运用Window类来生成OVER句子,然后灵舟生成Lag来从第n行中挑选一个值,在本例中是前一行,因为咱们将n指定为1。

不幸的是,因为SQL规范束缚,咱们不能在窗口函数上进行过滤。可是,这能够经过运用通用表表达式(CTEs)来弥补,可是Django现在不支撑这种办法。

运用自界说数据库函数进行扩展

有时分Dja手腕疼是怎么回事ngo ORM不答应你做你想做的工作。在许多状况下,实践上能够经过扩展内置根本类型来增加自己的功用。例如,假如你想运用Django没有揭露的自界说SQL函数,这能够经过子类化Func函数来轻松增加:

这便是所需的悉数。现在,咱们能够像运用任何Django揭露的SQL函数相同运用它。

咱们也能够用更杂乱的函数来扩展。假定咱们用独自的日期和时刻列建立了一个模previous型,可是需求将其与运用日期时刻的另一个表进行比较。咱们能够经过完成一个自界说函数来完成这一点。没有跨数据库的通用办法能够做到这一点,可是Django答应咱们为想要支撑的每个数据库别离完成这一点。下面是用于PostgreSQL和Sqlite的比如:

接着,咱们就能够运用这个函数来在QuerySet上注释一个日期时刻:

运转自界说SQL

假如咱们想要做的工作运用Django供给给咱们的功用不能直接完成,咱们有时能够直接编写自界说SQL。Django供给了多种完成办法。例如,咱们能够用一个自界说SQL表达式来对QuerySet进行注解:

咱们也能够用QuerySet的extra办法来做相同的工作:

extra有许多的选项,所以请查阅文档。

假如咱们想自己编写整个SQL查询,这也是一个选项。回来的列将按称号映射到模型字段。任何与现有字魏斯晴段名不匹配的列都将作为注解字段增加。

假如咱们不想回来模型目标,咱们也能够直接在数据库游标上运转自界说SQL查询:

这将回来一个包括列的元组。

自界说搬迁

向Django增加额定数据库功用的另一种办法是编写自界说搬迁。在Django取得对自界说束缚和索引的支撑之前,在自界说搬迁中运用RunSQL是向数据库增加这类选项的一种办法。另一种或许性是运用RunSQL向数据库增加自界说视图,并将其作为一个模型揭露:

自界说搬迁的另一个用例是数据搬迁。假如你的数据库中有一组一直可用的初始数据,那你能够运用数据搬迁将其增加到自定崇奉义搬迁中。假如你在运转测验时运用搬迁,那么相同的初始数据集也将在测验中可用。要做到这一点,你能够在你的搬迁文件中运用RunPython类:

我将向你展现的与搬迁相关的终究一个根本类型是SeparateDatabaseAndState类。Django在运转搬迁时,除了数据库中的实践状况外,还保存一个内部状况来表明模型的预期布局等。运用这个类,你能够别离修正这两个状况。假如你需求将这两者分隔,例如出于高可用性的原因,这个类将十分有用。这就需求用到列表,一个在内部状况下履行操作,一个在数据库中履行操作:

总结

我期望本文为你供给了一些关于在运用Django ORM时怎么优化或改善数据库查询的主意。

这绝不是一个完好的列表,所以请检查Django文档(https://docs.djangoproject.com/ )获取具体内容。我主张从QuerySet API reference(https://docs.djangoproject.com/en/2.2/ref/models/querysets/ 0部分隔端,然后从那里跟从链接去检查可用的API。

英文原文:https://qiniumedia.freelycode.com/vcdn/1/%E4%BC%98%E8%B4%A8%E6%96%87%E7%AB%A0%E9%95%BF%E5%9B%BE3/pushing-django-orm-to-its-limit.pdf 译者:郁闷的红秋裤