Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在查询子模型属性时,如何减少Django的DB调用次数?_Python_Django_Python 3.x_Django Models_Django Orm - Fatal编程技术网

Python 在查询子模型属性时,如何减少Django的DB调用次数?

Python 在查询子模型属性时,如何减少Django的DB调用次数?,python,django,python-3.x,django-models,django-orm,Python,Django,Python 3.x,Django Models,Django Orm,我很难找到基本的优化方法,如果有人能给我提供一些见解或者给我指出正确的方向,我将不胜感激 简化模型: class TimeStampedModel(models.Model): created = models.DateTimeField(auto_now_add=True, db_index=True) modified = models.DateTimeField(auto_now=True) class Meta: abstract = True

我很难找到基本的优化方法,如果有人能给我提供一些见解或者给我指出正确的方向,我将不胜感激

简化模型:

class TimeStampedModel(models.Model):
    created = models.DateTimeField(auto_now_add=True, db_index=True)
    modified = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True

class Venue(TimeStampedModel):
    name = models.CharField(unique=True, max_length=200, db_index=True)

class Offer(TimeStampedModel):
    venue_associated = models.ForeignKey(Venue, on_delete=models.CASCADE, db_index=True)
    content = models.TextField(max_length=500, db_index=True)
简化视图:

class MapView(ListView):
    fields = ["name"]
    model = Venue
    template_name = "venues/venue_map.html"
简化模板:

{% for venue in venue_list %}
    {{ venue.name }}
    {{ venue.offer_set.latest.created }}
    {{ venue.offer_set.latest.content }}
{% endfor %}
这会产生大量的数据库调用(~400)。浏览整个场馆列表只会创建一个呼叫(+1个未关联),而两个套餐呼叫会创建新呼叫(每个200个)

因此,我认为为场馆模型创建一个单独的属性“latest”会有所帮助,因为它至少可以处理“latest”调用加倍的问题,但不会。我还尝试过覆盖通用的ListView方法,但没有得到任何结果

可能有一种方法我没有看到。目前,我所能想到的只是在场馆模型中添加额外的字段来复制信息,并需要额外的逻辑来管理它

编辑:

我试过: queryset=场馆.objects.prefetch\u相关('offer\u set')

它所做的只是创建一个附加查询:

SELECT ••• FROM "offers_offer" WHERE "offers_offer"."venue_associated_id" IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200)
相同的400个查询(具有不同的id值)仍在运行:

SELECT ••• FROM "offers_offer" WHERE "offers_offer"."venue_associated_id" = 1 ORDER BY "offers_offer"."order" ASC, "offers_offer"."created" DESC LIMIT 1

通常,您可以在一个额外的查询中获取所有相关模型,使用
预取相关(..)
,因此我们可以将其添加到
列表视图的
queryset
属性中:

class MapView(ListView):
    fields = ["name"]
    model = Venue
    queryset = Venue.objects.prefetch_related('offer_set')
    template_name = "venues/venue_map.html"
类映射视图(ListView):
字段=[“名称”]
模型=场地
queryset=场馆.objects.prefetch\u相关('offer\u set')

template_name=“viouses/viouse\u map.html”
您正在访问关系数据,在django中,如果您使用普通查询访问相关数据,它将一次又一次地命中db hit。为此,您可以对特定关系使用与预取_相关的查询,它一次完成该表的所有数据库,当您对该项进行迭代时,它不会再次命中数据库。所有数据将在一次查询中显示

 queryset = Venue.objects.prefetch_related('offer_set')

Django提供了类似于
select_related
prefetch_related
的结构,以优化相关的对象查询操作。在您的情况下,它应该是:

   queryset = Venue.objects.all().prefetch_related('offer_set')
对于最新的项目,您是否尝试在
场馆
模型中添加
订购
元值,如下所示:

class Offer(TimeStampedModel):
    ......

    class Meta:
        ordering = ['created']


{% for venue in venue_list %}
    {{ venue.name }}

    {% with venue.offer_set|first as first_offer %}
    {{ first_offer.created }}
    {{ first_offer.content }}
     {% endwith %}

{% endfor %}

当您向
Django ORM
进行这些
查询时,让我们对Django中的幕后真实情况进行更细致的分析

queryset
是惰性的,意思是:(根据)

在内部,可以构造、过滤、切片查询集,并通常在不实际访问数据库的情况下传递查询集。在对queryset进行评估之前,实际上不会发生任何数据库活动

查询集
只能在执行以下任一操作时进行计算:
迭代
切片
酸洗
repr()
len()
list()
bool()

在这里,如果我们看到您正在执行的SQL查询:

PS:您可以通过在shell中进行以下日志记录来查看将命中数据库的SQL查询:

import logging
l = logging.getLogger('django.db.backends')
l.setLevel(logging.DEBUG)
l.addHandler(logging.StreamHandler())

result_queryset = Venue.objects.all()

SELECT "venue"."id"......(all model fields)......FROM "venue";
在这里,在上面的SQL查询中,除了
ForeignKeys的
id
之外,您不会看到任何
相关字段
对象

现在,如果您访问上述查询集的任何相关字段,ORM将再次访问数据库以获取它。这可以通过
预回迁相关()
选择相关()
来防止(两者之间的差异)

此外,每次模板要求
名称
提供集.latest.created
提供集.latest.content
,它都会因为对
查询集
的延迟评估而命中数据库

编辑:

由于您已经编辑了您的问题,您希望所有
场馆
对象作为一个整体,并且不希望400个查询访问您的数据库,我将建议一种不理想的方法-


在将查询集作为上下文传递给模板之前,请事先评估查询集。我已经在上面提到了很多方法。

我认为我的答案中的编辑会有所帮助。这是一个不理想的方式来完成你正在尝试的,但它会工作。编辑我的初始问题。最新的()仍然很可能是创建新查询的部分。有没有办法解决这个问题,或者有更好的办法呢?因为你提到了评估,多亏了这一点,我才更接近了。对视图的评估也会在模板中产生对附加逻辑的需求。因此,我选择了@cached_属性decorator,并在模型中使其失效。这不是一个理想的解决方案,但似乎比模板中的视图/逻辑中的评估更干净,并杀死了200个查询。所以进步了,谢谢。很高兴有帮助:)编辑了我最初的问题。最新的()仍然很可能是创建新查询的部分。是否有解决方法或更好的方法?为什么要获取
latest()
?对于每个地点,我的模板都需要最新的报价对象。@UssiSonad,我已更新了上面的答案。请让我知道,无论它是否有效。谢谢,经过编辑的答案稍加修改,解决了我的整个问题。从400到2个查询。我不得不先使用vention.offer|set.first而不是vention.offer|set | first,但它奏效了。非常感谢。编辑了我最初的问题。最新的()仍然很可能是创建新查询的部分。有办法解决这个问题还是有更好的办法?
result_queryset = Venue.objects.prefetch_related('offer_set')
# Django creates API accessors for the "other" side of the relationship
# Here, _set is used to access that "other" side i.e. related objects

A NEW SQL QUERY THAT FETCHES YOUR offer_set OBJECTS via a JOIN (that is how prefetch_related works)