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

时间:2018-02-13 13:23:46

标签: python django django-models django-forms django-queryset

Models.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 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)

如果选择的吩咐有20个PurOrderItem,则此设置会花费22个查询。

  • 1表示PurOrder实例,
  • 1表示PurOrderItem实例
  • 20,用于PurOrderItem的选定材料。

想一想,如果有1000个PurOrderItem

使用提供的select_related,它会向PurOrderItemselect添加材料,但是当我想它显示它时,它会再次查询。

我使用django-autocomplete-light,因此它可以避免查询所有材质实例,但它会不断查询所选材质,即使我选择了相关材质也能显示它。

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

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

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

更新

在我创建此问题后很长一段时间,我尝试了提供的解决方案。 问题是,formset的形式彼此不了解。因此,提供的查询集的selected_related或prefetch_related查找不会传递给表单集表单。

2 个答案:

答案 0 :(得分:5)

你很好。此代码只需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就是查询关系的最有效方式。

答案 1 :(得分:0)

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