Django - 有多少数据库命中导致使用模型方法,该方法通过外键在模板中访问另一个对象?
Django - How many DB hits cause using a model method, which access another object via Foreign Key, in template?
具体来说,调用一个通过外键访问另一个对象的模型方法,并在 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 模型有一种方法可以获取折扣金额,但要计算该金额,它必须访问其 Shipment。
(当然,我们可以将折扣作为一个字段存储在 Product 中,不再计算它,但为了这个问题,让我们忽略它)。
现在,假设我们有以下视图:
def viewShipments(request):
context = {
"shipments": Shipment.objects.all(),
}
render(request, "template.html", context)
我们只是将所有 Shipments 作为上下文传递。
最后,我们假设以下模板:
{% for shipment in shipments %}
{% for product in shipment.getProducts %}
{{ product.getDiscount }}
{% endfor %}
{% endfor %}
如果我对查询集工作原理的理解是正确的,那么在第一个循环中它会访问数据库以获取所有 Shipments(因此,到目前为止有 1 次访问)。然后在第二个循环中,它再次访问数据库以获取相应的产品,每个 Shipment 一次(因此,S 访问)。最后,每个产品调用其方法,该方法通过外键访问其 Shipment 以检索折扣,这(我假设)会导致另一个命中,每个产品一次(P 次)。最后一部分特别让我烦恼,因为我们再次检索每个 Shipment,我们已经在第一个 for 循环中完成了,但这次是一个一个地检索!
一共是(1 + S + (S*P))次数据库点击,好像有点多了。
这是正确的,还是 Django 以我不知道的方式优化了一些东西?我知道查询集是惰性的并且有缓存,但我不知道它们是否适用于这种情况。
提前致谢!
您可以使用 prefetch_related 优化数据库访问并更改 getProducts 以使用 Django 相关字段管理器。
prefetch 将在后台执行附加查询并检索相关产品,从而将数据库调用总数减少到 2。
https://docs.djangoproject.com/en/1.10/ref/models/querysets/#prefetch-related
如果您从 Product 对象访问了货件,您应该使用 select_related。 Select related 执行对象与另一个对象的连接并减少数据库调用的数量,但它仅适用于 1-1 relations/1->many(从另一侧)。
https://docs.djangoproject.com/en/1.10/ref/models/querysets/#select-related
https://docs.djangoproject.com/en/1.10/ref/models/relations/
怎么做?
考虑模型文件中的以下代码。请注意,我正在使用 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 及更高版本。
具体来说,调用一个通过外键访问另一个对象的模型方法,并在 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 模型有一种方法可以获取折扣金额,但要计算该金额,它必须访问其 Shipment。
(当然,我们可以将折扣作为一个字段存储在 Product 中,不再计算它,但为了这个问题,让我们忽略它)。
现在,假设我们有以下视图:
def viewShipments(request):
context = {
"shipments": Shipment.objects.all(),
}
render(request, "template.html", context)
我们只是将所有 Shipments 作为上下文传递。
最后,我们假设以下模板:
{% for shipment in shipments %}
{% for product in shipment.getProducts %}
{{ product.getDiscount }}
{% endfor %}
{% endfor %}
如果我对查询集工作原理的理解是正确的,那么在第一个循环中它会访问数据库以获取所有 Shipments(因此,到目前为止有 1 次访问)。然后在第二个循环中,它再次访问数据库以获取相应的产品,每个 Shipment 一次(因此,S 访问)。最后,每个产品调用其方法,该方法通过外键访问其 Shipment 以检索折扣,这(我假设)会导致另一个命中,每个产品一次(P 次)。最后一部分特别让我烦恼,因为我们再次检索每个 Shipment,我们已经在第一个 for 循环中完成了,但这次是一个一个地检索!
一共是(1 + S + (S*P))次数据库点击,好像有点多了。
这是正确的,还是 Django 以我不知道的方式优化了一些东西?我知道查询集是惰性的并且有缓存,但我不知道它们是否适用于这种情况。
提前致谢!
您可以使用 prefetch_related 优化数据库访问并更改 getProducts 以使用 Django 相关字段管理器。 prefetch 将在后台执行附加查询并检索相关产品,从而将数据库调用总数减少到 2。
https://docs.djangoproject.com/en/1.10/ref/models/querysets/#prefetch-related
如果您从 Product 对象访问了货件,您应该使用 select_related。 Select related 执行对象与另一个对象的连接并减少数据库调用的数量,但它仅适用于 1-1 relations/1->many(从另一侧)。 https://docs.djangoproject.com/en/1.10/ref/models/querysets/#select-related https://docs.djangoproject.com/en/1.10/ref/models/relations/
怎么做? 考虑模型文件中的以下代码。请注意,我正在使用 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 及更高版本。