Python Django-在模板中,使用model方法(通过外键访问另一个对象)会导致多少DB命中?
特别是,调用通过外键访问另一个对象的模型方法,并在for循环中多次调用该方法 让我们假设以下模型为例: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)
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更快,而且我在文档中找不到任何东西,那么有什么好消息呢?