Django表单集,每个表单的关联字段查询

17

模型.py

class Material(BaseModelClass):
    material = models.CharField(max_length=25, verbose_name='Material')
    def __str__(self):
        return self.material

class PurOrder(BaseModelClass):
    order_number = models.CharField(max_length=25)

class PurOrderItem(BaseModelClass):
    order = models.ForeignKey(PurOrder, on_delete=models.CASCADE)
    material = models.ForeignKey(Material, on_delete=models.PROTECT)

我创建了一个采购订单表单(PurOrder form),以及一个采购订单条目表单集(PurOrderItem formset)

PurOrderForm = modelform_factory(PurOrder, fields=('order_number',))
PurOrderFormset = inlineformset_factory(PurOrder, PurOrderItem,fields=('material',))

以下是它们的初始化方式。

form = PurOrderForm(instance=order_instance)
queryset = order_instance.purorderitem_set.all().select_related('material',)
formset = PurOrderFormset(instance=order_instance, queryset=queryset)

如果选择的purorder有20个PurOrderItem,则此设置将花费我22个查询。

  • 1个用于PurOrder实例,
  • 1个用于PurOrderItem实例,
  • 20个用于这些PurOrderItem的选定材料。

考虑一下,如果有1000个PurOrderItem。

使用提供的select_related,它会将材料添加到PurOrderItemselect中,但是当要显示它时,我认为它会再次查询。

我使用django-autocomplete-light,所以它可以避免查询所有材料实例,但它仍会查询所选材料,以便在选择相关材料时显示它。

理想情况下,我希望选择具有预取的purorderitem和相关材料的PurOrder实例,这意味着只需要3个查询。当轮到它们时,将使用预取的purorderitem和材料。

请建议我避免所选选择查询的方法。

注意:我试图避免使用缓存。

更新

很长时间之后,我尝试了提供的解决方案。问题是,formset的表格彼此不知道。因此,提供的queryset的selected_related或prefetch_related查找未传递到formset表格中。


我认为这与问题无关,可以省略添加dal小部件代码。 - durdenk
我认为相反地,这非常相关。 - Antoine Pinsard
1
我认为这个问题的答案是你正在寻找的:https://dev59.com/8WUp5IYBdhLWcg3wj4Du - solarissmoke
我认为你可以使用 prefetch_related 函数:https://docs.djangoproject.com/en/2.0/ref/models/querysets/#django.db.models.query.QuerySet.prefetch_related - Dan Lupascu
请参考以下链接:https://stackoverflow.com/q/38124092 - djvg
显示剩余2条评论
2个回答

5
你很棒。这段代码只需要3个查询。正如你在select_related()文档中所看到的:
返回一个QuerySet,它将“跟随”外键关系,在执行其查询时选择其他相关对象数据。这是一个性能增强器,它会导致单个更复杂的查询,但后来使用外键关系不需要数据库查询。
这意味着你的代码将执行mysql join,并生成包含所有数据的数据集。因此,我可以看出你的代码非常好。
我建议你使用一些性能分析工具,比如django-silk,来查看生成了多少查询。
脚注:
正如你在prefetch_related()文档中所看到的,prefetch_related()select_related()之间的区别在于它们执行连接的方式:
这(prefetch_related)的目的与select_related类似,都旨在停止访问相关对象时引起的大量数据库查询,但策略却大不相同。
... select_related通过创建SQL join并将相关对象的字段包含在SELECT语句中来工作。因此,select_related在同一数据库查询中获取相关对象。
...
另一方面,prefetch_related为每个关系执行单独的查找,并在Python中进行“连接”。
只要你需要one-to-one关系,select_related就是查询关系的最有效方式。

1
Gal,你自己用过这个答案吗?我没能让它正常工作。Django表单集不知道彼此之间的关系,也不知道base_model。因此,如果没有传递select_related和prefetch,就无法在表单集中使用。这是我的经验。 - durdenk

0

最近我意识到,在类似的情况下问题并不在于表单集,而是模板中的{{formset.errors}},因为每个表单集都会为此生成一个查询。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接