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之路——form modelform formset modelformset的各种用法
使用 clean 更改 Django formset 中的字段
Django formset_factory vs modelformset_factory vs inlineformset_factory