Django Inline for ManyToMany generate duplicate queries

后端 未结 3 769
南方客
南方客 2021-01-06 01:50

I\'m experiencing some major performing issue with my django admin. Lots of duplicate queries based on how many inlines that I have.

models.py

class          


        
3条回答
  •  不知归路
    2021-01-06 02:38

    I've assembled a generic solution based on @makaveli's answer that doesn't seem to have problem mentioned in the comments:

    class CachingModelChoicesFormSet(forms.BaseInlineFormSet):
        """
        Used to avoid duplicate DB queries by caching choices and passing them all the forms.
        To be used in conjunction with `CachingModelChoicesForm`.
        """
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            sample_form = self._construct_form(0)
            self.cached_choices = {}
            try:
                model_choice_fields = sample_form.model_choice_fields
            except AttributeError:
                pass
            else:
                for field_name in model_choice_fields:
                    if field_name in sample_form.fields and not isinstance(
                        sample_form.fields[field_name].widget, forms.HiddenInput):
                        self.cached_choices[field_name] = [c for c in sample_form.fields[field_name].choices]
    
        def get_form_kwargs(self, index):
            kwargs = super().get_form_kwargs(index)
            kwargs['cached_choices'] = self.cached_choices
            return kwargs
    
    
    class CachingModelChoicesForm(forms.ModelForm):
        """
        Gets cached choices from `CachingModelChoicesFormSet` and uses them in model choice fields in order to reduce
        number of DB queries when used in admin inlines.
        """
    
        @property
        def model_choice_fields(self):
            return [fn for fn, f in self.fields.items()
                if isinstance(f, (forms.ModelChoiceField, forms.ModelMultipleChoiceField,))]
    
        def __init__(self, *args, **kwargs):
            cached_choices = kwargs.pop('cached_choices', {})
            super().__init__(*args, **kwargs)
            for field_name, choices in cached_choices.items():
                if choices is not None and field_name in self.fields:
                    self.fields[field_name].choices = choices
    

    All you'll need to do is subclass your model from CachingModelChoicesForm and use CachingModelChoicesFormSet in your inline class:

    class ArrangementInlineForm(CachingModelChoicesForm):
        class Meta:
            model = Arrangement
            exclude = ()
    
    
    class ArrangementInline(admin.TabularInline):
        model = Arrangement
        extra = 50
        form = ArrangementInlineForm
        formset = CachingModelChoicesFormSet
    

提交回复
热议问题