What is the best way to use `ModelChoiceField` in `NestedTabularInline`
Recently, I've noticed that the detail screen in my web has been performing very slowly. After analyzing the issue, I found that it's caused by an inline
containing a ModelChoiceField
when number of instances too large. This field repeatedly executes SELECT *
queries to the database, significantly slowing down the screen's loading time.
Eg: That inline have 50 instances, now it has 50 ModelChoiceField
that's ramping 50 SELECT *
I tried various solutions, including setting the queryset to objects.none()
However, this approach led to a new problem: when editing, the data doesn't auto-select as expected. I attempted to address this by setting the initial value dynamically in the __init__
method, but it didn't work due to the lack of a queryset.
class ProductOptionForm(CustomDuplicateItemInlineForm):
class Meta:
model = ProductOption
fields = '__all__'
# override option để hiển thị autocomplete với size option
option = forms.ModelChoiceField(
queryset=PodOption.objects.none(),
required=True,
widget=CustomOptionSelect2(
url='product-size-options-auto-complete',
),
label='Size Option'
)
def __init__(self, *args, **kwargs):
super(ProductOptionForm, self).__init__(*args, **kwargs)
# Use the preloaded data to set the initial dynamically
if self.instance and self.instance.pk and hasattr(self, 'product_size_options'):
self.fields['option'].initial = self.product_size_options[self.instance.option_id] # Manually set the result cache
It's all empty as you can see
If anyone knows the solution, please let me know. Thanks in advance!
After some debugging, it seems filter_choices_to_render
of autocomplete.Select2
is the one causing trouble, so I override it with pre-loaded value. This method doesn't look good but it's worked for now
class CustomOptionSelect2(autocomplete.Select2):
def filter_choices_to_render(self, selected_choices):
"""Override filter_choices_to_render to prevent access database on initial."""
if hasattr(self, 'initial'):
value = ModelChoiceIteratorValue(
self.initial.pk, self.initial
)
self.choices = [
(value, f"{self.initial.type}: {self.initial.value}")
]
else:
super().filter_choices_to_render(selected_choices)