【问题标题】:django forms prefetch on cleaned datadjango 表单预取已清理的数据
【发布时间】:2015-12-18 11:08:18
【问题描述】:

我正在努力提高我的 Django 应用程序产生的数据库点击量,而我的抱怨之一是 Django 表单。

当我获取一个带有表单的页面时,它会从数据库中加载对象以填写ModelChoiceFields,这很棒。

当我发布一些表单数据时,表单会清理数据。现在,在表单的clean_foo 方法中,我想访问foo 对象关系之一:foo.bar。这将访问数据库以获取 bar 对象。

有什么方法可以让我预取bar?我的意思是当表单使用pk 来查找foo 对象时,我可以让它预取bar 吗?我可以在哪里做呢?

查看Django source code,似乎选择的对象是直接用.get()获取的,而不是作为.filter()的查询集

def to_python(self, value):
    if value in self.empty_values:
        return None
    try:
        key = self.to_field_name or 'pk'
        value = self.queryset.get(**{key: value})    # <-- Right here
    except (ValueError, TypeError, self.queryset.model.DoesNotExist):
        raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice')
    return value

所以,这告诉我的是,我不应该在那里尝试任何事情。我能做的最好的事情是

def clean_foo(self):
    foo = Foo.objects.filter(pk=self.cleaned_data['foo'].pk).select_related('bar')
    [...]

在那里我可以预取其余逻辑所需的内容。所以它不会是 1 次查询,但我最多可以进行 2 次查询。

我意识到这开始听起来像是一个陈述而不是一个问题,所以如果可能的话,请证明我错了

【问题讨论】:

    标签: python django forms


    【解决方案1】:

    您似乎可以在字段的查询集定义中直接使用select_related

    class MyForm(forms.ModelForm):
        my_field = forms.ModelChoiceField(queryset=Foo.objects.select_related('bar'))
    

    【讨论】:

    • 哦,对了。它实际上在获取对象时使用self.queryset。哦,天哪,这很简单
    【解决方案2】:

    编辑:这是一个过于复杂的想法,按照 Daniel 的建议传递一个合适的查询集要简单得多。

    您可以继承 ModelChoiceField 并覆盖 to_python 以便它执行 select_related

    除非您的表单中有很多外键,或者您的页面、字段中有很多表单,否则保存一个查询可能不值得花费额外的复杂性。

    一个例子可能看起来像

    class MyModelChoiceField(forms.ModelChoiceField):
        def __init__(self, *args, select_related=(), prefetch_related=(), **kwargs):
            super(MyModelChoiceField, self).__init__(*args, **kwargs):
            self._selects = select_related
            self._prefetches = prefetch_related
    
        def to_python(self, value):
            if value in self.empty_values:
                return None
            try:
                key = self.to_field_name or 'pk'
                values = self.queryset.filter(
                    **{key: value}
                ).select_related(
                    *self._selects
                ).prefetch_related(
                    *self._prefetches
                )
                value = values.first()
            except (ValueError, TypeError, self.queryset.model.DoesNotExist):
                raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice')
            return value
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-23
      • 1970-01-01
      • 2012-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-13
      • 1970-01-01
      相关资源
      最近更新 更多