首页 > 解决方案 > 减少 django modelform 中的重复查询

问题描述

我有一个模型表单来在我的基于函数的视图中创建模型实例。当创建视图呈现表单模板时,它会运行 6 个我想减少的查询。有什么办法可以减少 qs 或提高模型创建视图的性能?

模型.py

class Teacher(models.Model):
    name = models.CharField(max_length=150)
    photo = models.ImageField(upload_to='teachers',
                              default='teacheravatar.jpg')
    date_of_birth = models.DateField(blank=True, null=True)
    designation = models.ForeignKey(Designation, on_delete=models.CASCADE)
    expertise = models.ManyToManyField(
        to=Topic, blank=True, related_name='expert_in')
    mobile = models.CharField(max_length=11, blank=True, null=True)
    email = models.CharField(max_length=255, blank=True, null=True)
    joining_date = models.DateField(auto_now=True)

    class Meta:
        ordering = ['joining_date', 'name']

* 表格.py *

class TeacherForm(ModelForm):
    class Meta:
        model = Teacher
        fields = ['name', 'photo', 'date_of_birth',
                  'designation', 'expertise',
                  'mobile', 'email', ]

* 视图.py *

# THIS VIEW DUPLICATES QUEREIS
# AND RUNS 6 QUERIES
@login_required
def add_teacher_view(request):
    """
    :param request:
    :return: teacher add form
    """
    if request.method == 'POST':
        form = TeacherForm(request.POST)
        if form.is_valid():
            form.save()
            pk = form.instance.pk
            return redirect('teachers:teacher_details', pk=pk)
    form = TeacherForm()
    context = {'form': form}
    return render(request, 'teachers/add_teacher.html', context)

标签: pythondjangoquery-optimizationquery-performance

解决方案


您应该在关系字段上定义 .choices。当您在模型表单中有关系字段时,django 模型表单字段在 request.get 方法中运行重复查询。事实上,我们通过手动提供数据来防止字段运行重复查询。

模型.py

class TeacherForm(ModelForm):
    class Meta:
        model = Teacher
        fields = ['name', 'photo', 'date_of_birth',
                  'designation', 'expertise',
                  'mobile', 'email', ]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        designation_choices = [(designation.id, designation.__str__()) for designation in Designation.objects.all()]
        expertise_choices = [(topic.id, topic.__str__()) for topic in Topic.objects.all()] 
        self.fields['designation'].choices = designation_choices
        self.fields['expertise'].choices = expertise_choices

我建议您在所有模型中定义str,例如,假设您在所有模型的str中返回 self.name,您可以像这样定义 designation_choices 和 knowledge_choices:

        designation_choices = Designation.objects.values_list('id', 'name')
        expertise_choices = Topic.objects.values_list('id', 'name')

这是如此之快,因为查询所有字段的名称或主题只查询其中的两个字段。

更深

(通过参考 django 课程代码来回答,仅供阅读)

django modelform 创建表单字段 ModelChoiceField、ModelMultipleChoiceField for ForeignKey、ManyToManyField 字段。这两个字段都使用方法 _get_choices 来创建那些重复的查询。

由 self.iterator(self) 运行的重复查询这里的关键是当提供 _choices 时,默认查询(重复查询)将停止。此处的属性选择将填充变量 _choices 并删除重复的查询。

django.forms.models.py

    def _get_choices(self):
        # If self._choices is set, then somebody must have manually set
        # the property self.choices. In this case, just return self._choices.
        if hasattr(self, '_choices'):
            return self._choices

        # Otherwise, execute the QuerySet in self.queryset to determine the
        # choices dynamically. Return a fresh ModelChoiceIterator that has not been
        # consumed. Note that we're instantiating a new ModelChoiceIterator *each*
        # time _get_choices() is called (and, thus, each time self.choices is
        # accessed) so that we can ensure the QuerySet has not been consumed. This
        # construct might look complicated but it allows for lazy evaluation of
        # the queryset.
        return self.iterator(self)

    choices = property(_get_choices, ChoiceField._set_choices)

推荐阅读