Python Django-在模板中,使用model方法(通过外键访问另一个对象)会导致多少DB命中?

Python Django-在模板中,使用model方法(通过外键访问另一个对象)会导致多少DB命中?,python,django,templates,django-queryset,Python,Django,Templates,Django Queryset,特别是,调用通过外键访问另一个对象的模型方法,并在for循环中多次调用该方法 让我们假设以下模型为例: class Shipment(Model): discount = FloatField() def getProducts(self): return Product.objects.filter(shipment = self) class Product(Model): shipment = ForeignKey(Shipment)

特别是,调用通过外键访问另一个对象的模型方法,并在for循环中多次调用该方法

让我们假设以下模型为例:

class Shipment(Model):

    discount = FloatField()

    def getProducts(self):
        return Product.objects.filter(shipment = self)

class Product(Model):

    shipment = ForeignKey(Shipment)
    baseCost = IntegerField()

    def getDiscount(self):
        return self.baseCost * self.shipment.discount
一批货对每种产品都有一定的折扣率。产品模型有一种方法可以获得折扣金额,但要计算折扣金额,它必须访问其发货

(当然,我们可以将折扣存储为Product中的一个字段,这样就不会再计算它,但是为了这个问题,我们可以忽略它)

现在,假设我们有以下观点:

def viewShipments(request):

    context = {
        "shipments": Shipment.objects.all(),
    }

    render(request, "template.html", context)
我们只是将所有装运作为上下文传递

最后,让我们假设以下模板:

{% for shipment in shipments %}

    {% for product in shipment.getProducts %}

        {{ product.getDiscount }}

    {% endfor %}

{% endfor %}
如果我对Querysets如何工作的理解是正确的,那么在第一个循环中,它将命中DB以获取所有装运(因此,到目前为止有1次命中)。然后在第二个循环中,它再次点击DB以获得相应的产品,每次装运一次(因此,S点击)。最后,每个产品调用其方法,该方法通过外键访问其发货以检索折扣(我假设),这会导致另一次命中,每个产品一次(P次)。最后一部分让我特别恼火,因为我们正在再次检索每一批货,这是我们在第一个for循环中已经做过的,但这次是一个接一个

总的来说,是(1+s+(s*p))数据库命中,这似乎有点过分

这是正确的,还是Django以我不知道的方式优化了事情?我知道QuerySet是惰性的,并且有一个缓存,但我不知道它们是否适用于这种情况


提前谢谢

您可以使用与预取相关的优化数据库访问,并将getProducts更改为使用与Django相关的Field manager。 预取将在后台执行额外的查询,并检索相关产品,从而将DB调用总数减少到2

如果您已从产品对象访问装运,则应使用选择与之相关的。Select related执行对象与另一个对象的连接,并减少DB调用的数量,但它仅适用于1-1关系/1->many(从另一端)。

怎么做? 在模型文件中考虑下面的代码。请注意,我正在使用Django RelatedManager并使用self访问它

class Shipment(Model):

    discount = FloatField()

    def getProducts(self):
        return self.product_set.all()

class Product(Model):
...
现在神奇的事情发生了,我们想让django把所有的产品和货物一起拿到手

def viewShipments(request):

    context = {
        "shipments": Shipment.objects.prefetch_related('product_set').all(),
    }

    render(request, "template.html", context)

请注意,必须使用Django 1.4及更高版本。

您可以使用与预回迁相关的
优化数据库访问,并将getProducts更改为使用与Django相关的Field manager。 预取将在后台执行额外的查询,并检索相关产品,从而将DB调用总数减少到2

如果您已从产品对象访问装运,则应使用选择与之相关的。Select related执行对象与另一个对象的连接,并减少DB调用的数量,但它仅适用于1-1关系/1->many(从另一端)。

怎么做? 在模型文件中考虑下面的代码。请注意,我正在使用Django RelatedManager并使用self访问它

class Shipment(Model):

    discount = FloatField()

    def getProducts(self):
        return self.product_set.all()

class Product(Model):
...
现在神奇的事情发生了,我们想让django把所有的产品和货物一起拿到手

def viewShipments(request):

    context = {
        "shipments": Shipment.objects.prefetch_related('product_set').all(),
    }

    render(request, "template.html", context)

请注意,您必须使用Django 1.4及更高版本。

我认为select_related()在这里可能比prefetch_related()更合适。此外,您还可以在shipping=ForeignKey(shipping,related_name=“products”)中添加相关的_name,然后使用self.products.all()。我制作了一个快速文本,其中包含3个装运,每个装运3个产品。正如我所怀疑的,我通常会收到13个询问。使用预回迁和相关经理一起获取产品,实际上可以归结为两个查询。然而,仅仅通过使用相关的管理器,没有预取,它就从13个查询减少到了6个查询。我不知道相关管理器比使用查询集更快,而且我在文档中找不到任何东西,所以给出了什么?我认为选择相关()可能比预取相关()更合适。此外,您还可以在shipping=ForeignKey(shipping,related_name=“products”)中添加相关的_name,然后使用self.products.all()。我制作了一个快速文本,其中包含3个装运,每个装运3个产品。正如我所怀疑的,我通常会收到13个询问。使用预回迁和相关经理一起获取产品,实际上可以归结为两个查询。然而,仅仅通过使用相关的管理器,没有预取,它就从13个查询减少到了6个查询。我不知道相关经理比使用QuerySet更快,而且我在文档中找不到任何东西,那么有什么好消息呢?