Django formset,查询每个表单的关系字段

Posted

技术标签:

【中文标题】Django formset,查询每个表单的关系字段【英文标题】:Django formset , queries for relational field for every form 【发布时间】:2018-07-23 21:14:18 【问题描述】:

模型.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 表单和 PurOrderItem 表单集

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)

如果选定的采购订单有 20 个 PurOrderItem,则此设置会花费我 22 个查询。

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

想一想,如果有 1000 个 PurOrderItem

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

我使用 django-autocomplete-light,因此它可以让我免于查询所有材质实例,但它会继续查询选定的材质,以显示它,即使我选择了相关的材质。

理想情况下,我会选择带有预取 purorderitem 和相关材料的 PurOrder 实例,这意味着 3 个查询。当轮到他们时,将使用预取的 purorderitem's 和 material's。

请告诉我一种避免选择查询的方法。

注意:我尽量避免在这里缓存。

更新

在我创建这个问题并尝试提供的解决方案之后很长时间。 问题是,formset 的表单彼此不知道。因此,提供的查询集的 selected_related 或 prefetch_related 查找不会传递给 formset 表单。

【问题讨论】:

您如何使用django-autocomplete-light?我在您的 PutOrderFormset 中没有看到任何 dal 小部件。 我认为它与质疑和省略添加 dal 小部件代码无关。 相反,我认为它非常相关。 我认为这个问题的答案就是你要找的:***.com/questions/15203207/… 我想你可以使用prefetch_related函数:docs.djangoproject.com/en/2.0/ref/models/querysets/… 【参考方案1】:

你很好。此代码将只花费您 3 次查询。 正如您在select_related() 文档中看到的那样:

返回一个 QuerySet,它将“遵循”外键关系,在执行查询时选择其他相关对象数据。这是一个性能提升器,它会导致单个更复杂的查询,但意味着以后使用外键关系将不需要数据库查询。

这意味着您的代码将执行mysql join,并将生成包含所有数据的数据集。因此,我可以看到您的代码非常好。

我建议您使用诸如django-silk 之类的分析来查看生成了多少查询。

脚注:

正如您在 prefetch_related() 文档中所见,prefetch_related()select_related() 之间的区别在于它们执行连接的方式:

这个 (prefetch_related) 与 select_related 的目的相似,因为两者都旨在阻止由访问相关对象引起的大量数据库查询,但是策略完全不同。

...

select_related 通过创建 SQL 连接并包含字段来工作 SELECT 语句中的相关对象。为此原因, select_related 获取同一数据库查询中的相关对象。

...

prefetch_related,另一方面,对每个关系进行单独的查找,并在 Python 中进行“连接”。

所以只要你需要one-to-one关系,select_related是查询关系最有效的方式。

【讨论】:

Gal,你自己用过这个答案吗?我无法让它工作。 Django 表单集不知道彼此不是 base_model。因此,提供的 select_related 和预取并没有传递给表单集。这就是我所经历的。【参考方案2】:

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

【讨论】:

以上是关于Django formset,查询每个表单的关系字段的主要内容,如果未能解决你的问题,请参考以下文章

Django formset 设置当前用户

Django之路——form modelform formset modelformset的各种用法

使用 clean 更改 Django formset 中的字段

在 django formset 中动态添加行

Django formset_factory vs modelformset_factory vs inlineformset_factory

从 django formset 中删除表单