通过预取和 select 相关提高性能
Increase performance with prefetch and select related
我正在尝试导出一个包含模型订单信息的 csv,该模型订单与送货渠道和餐厅具有一对一的关系,与订单行具有一对多的关系。下载它花费了太多时间(10k 行大约需要 20 秒)。
这是我的代码:
orderlines = OrderLine.objects.select_related("product").only(
"product__display_name", "quantity", "paid_amount", "discount_amount"
)
return (
Order.objects.prefetch_related(Prefetch("orderlines", queryset=orderlines, to_attr="orderlines_list"))
.select_related("delivery_channel")
.select_related("restaurant")
)
这些是我的模型:
class Order(TimeStampedModel, TenantModel):
id = models.AutoField
objects = OrderManager()
restaurant = models.ForeignKey(
Restaurant,
blank=False,
null=False,
on_delete=models.PROTECT,
help_text=_("References the restaurant the order is for"),
)
delivery_channel = models.ForeignKey(
DeliveryChannel,
blank=False,
null=False,
on_delete=models.PROTECT,
)
class Restaurant(TenantModel):
id = models.AutoField
name = models.CharField(max_length=255, blank=False, null=False, default="", unique=False)
class DeliveryChannel(models.Model):
id = models.AutoField
name = models.CharField(
max_length=255, blank=False, null=False, unique=True, default="",
)
class OrderLine(TimeStampedModel, TenantModel):
id = models.AutoField
order = models.ForeignKey(
Order,
blank=False,
null=False,
on_delete=models.CASCADE,
related_name="orderlines",
)
product = models.ForeignKey(
Product, blank=False, null=False, on_delete=models.CASCADE,
)
unit_price = models.DecimalField(
blank=False,
null=False,
max_digits=8,
decimal_places=2,
)
quantity = models.IntegerField(blank=False, null=False, unique=False)
paid_amount = models.DecimalField(
blank=False,
null=False,
max_digits=8,
decimal_places=2,
)
discount_amount = models.DecimalField(
blank=False,
null=False,
max_digits=8,
decimal_places=2,
help_text=_("Amount of the discount applied to the product"),
)
class Product(TenantModel):
id = models.AutoField
objects = ProductManager()
display_name = models.CharField(max_length=255, blank=False, null=False, unique=False)
我想过最后只使用它,但我不能在订单上使用它,因为它是一对多的关系。我坚持如何提高性能。非常感谢。
你可以使用.only(…)
[Django-doc],但你需要将外键添加到模型本身,否则这将导致另一个 N+1 问题:它每次都必须在哪里进行查询以确定它属于什么Order
,所以:
orderlines = OrderLine.objects.select_related('product').only(
'product__display_name',
# add ForeignKey to the Order object ↓
'quantity', 'paid_amount', 'discount_amount'<b>, 'order'</b>
)
return Order.objects.prefetch_related(
Prefetch('orderlines', queryset=orderlines, to_attr='orderlines_list')
).select_related(
'delivery_channel'
'restaurant'
)
我正在尝试导出一个包含模型订单信息的 csv,该模型订单与送货渠道和餐厅具有一对一的关系,与订单行具有一对多的关系。下载它花费了太多时间(10k 行大约需要 20 秒)。
这是我的代码:
orderlines = OrderLine.objects.select_related("product").only(
"product__display_name", "quantity", "paid_amount", "discount_amount"
)
return (
Order.objects.prefetch_related(Prefetch("orderlines", queryset=orderlines, to_attr="orderlines_list"))
.select_related("delivery_channel")
.select_related("restaurant")
)
这些是我的模型:
class Order(TimeStampedModel, TenantModel):
id = models.AutoField
objects = OrderManager()
restaurant = models.ForeignKey(
Restaurant,
blank=False,
null=False,
on_delete=models.PROTECT,
help_text=_("References the restaurant the order is for"),
)
delivery_channel = models.ForeignKey(
DeliveryChannel,
blank=False,
null=False,
on_delete=models.PROTECT,
)
class Restaurant(TenantModel):
id = models.AutoField
name = models.CharField(max_length=255, blank=False, null=False, default="", unique=False)
class DeliveryChannel(models.Model):
id = models.AutoField
name = models.CharField(
max_length=255, blank=False, null=False, unique=True, default="",
)
class OrderLine(TimeStampedModel, TenantModel):
id = models.AutoField
order = models.ForeignKey(
Order,
blank=False,
null=False,
on_delete=models.CASCADE,
related_name="orderlines",
)
product = models.ForeignKey(
Product, blank=False, null=False, on_delete=models.CASCADE,
)
unit_price = models.DecimalField(
blank=False,
null=False,
max_digits=8,
decimal_places=2,
)
quantity = models.IntegerField(blank=False, null=False, unique=False)
paid_amount = models.DecimalField(
blank=False,
null=False,
max_digits=8,
decimal_places=2,
)
discount_amount = models.DecimalField(
blank=False,
null=False,
max_digits=8,
decimal_places=2,
help_text=_("Amount of the discount applied to the product"),
)
class Product(TenantModel):
id = models.AutoField
objects = ProductManager()
display_name = models.CharField(max_length=255, blank=False, null=False, unique=False)
我想过最后只使用它,但我不能在订单上使用它,因为它是一对多的关系。我坚持如何提高性能。非常感谢。
你可以使用.only(…)
[Django-doc],但你需要将外键添加到模型本身,否则这将导致另一个 N+1 问题:它每次都必须在哪里进行查询以确定它属于什么Order
,所以:
orderlines = OrderLine.objects.select_related('product').only(
'product__display_name',
# add ForeignKey to the Order object ↓
'quantity', 'paid_amount', 'discount_amount'<b>, 'order'</b>
)
return Order.objects.prefetch_related(
Prefetch('orderlines', queryset=orderlines, to_attr='orderlines_list')
).select_related(
'delivery_channel'
'restaurant'
)