Как решить проблему задержки при использовании django M2M и filter_horizontal в панели ModelAdmin?
Я использовал django ModelAdmin с отношениями M2M и код фильтрации полей формы следующим образом: Но для суперпользователя или любого другого логина, где количество почтовых ящиков превышает 1 lakh, я нарезал доступные после фильтрации. Но загрузка m2m-поля занимает время и не работает для логина суперпользователя:
def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.name == "mailboxes":
if request.user.is_superuser:
queryset = Mailbox.objects.all().only('id','email')
kwargs["queryset"] = queryset
field = super().formfield_for_manytomany(db_field, request, **kwargs)
field.widget.choices.queryset = queryset[:300] # Limit visible options
return field
if request.user.groups.filter(name__in=['customers']).exists():
queryset = Mailbox.objects.only('id', 'email').filter(
domain__customer__email=request.user.email
)
kwargs["queryset"] = queryset
field = super().formfield_for_manytomany(db_field, request, **kwargs)
field.widget.choices.queryset = queryset[:500] # Limit visible options
return field
return super().formfield_for_manytomany(db_field, request, **kwargs)
Я хочу использовать только filter_horizontal, а не django auto_complete_light или любой javascript.Как можно решить проблему задержки. Как вы можете видеть, фильтрация набора запросов уже выполнена, чтобы получить допустимые варианты, а затем нарезана.
Получение слишком большого количества строк данных создает нагрузку на сервер.
Возможно, будет полезно контролировать количество данных, получаемых за один раз, следующим образом:
a = []
for i in range(Model.objects.filter().count())[::1000]:
a += list(Model.objects.filter()[i:i+1000])
Ограничение количества объектов, загружаемых при инициализации формы, выглядит так:
class GroupMailIdsForm(forms.ModelForm):
class Meta:
model = GroupMailIds
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
request = getattr(self, 'request', None)
if not request:
return
email = request.user.email
qs = Mailbox.objects.all()
if request.user.is_superuser:
filtered_qs = qs
else:
filtered_qs = qs.none() # No access if none of the conditions match
# Set the filtered queryset to the form field
self.fields['mailboxes'].queryset = filtered_qs
В ModelAdmin:
def get_form(self, request, obj=None, **kwargs):
# Pass the request to the form
form = super().get_form(request, obj, **kwargs)
form.request = request # Attach the request to the form
return form
Инициализация для не-суперпользователя инициализирует набор запросов значением None, а во время администрирования заполняет выпадающий список необходимыми значениями, отфильтрованными во время администрирования на основе значения логина
Проблема задержки возникает из-за того, что filter_horizontal отображает на странице все доступные варианты поля mailboxes. Когда суперпользователь имеет доступ к более чем 100 тыс. экземпляров почтовых ящиков, это приводит к значительным проблемам с производительностью, включая медленную загрузку и таймауты.
Даже если вы отфильтровали набор запросов, суперпользователь все равно видит все объекты Mailbox, а filter_horizontal не рассчитан на эффективную обработку таких больших наборов данных без усовершенствований JavaScript.
Поскольку вы предпочитаете не использовать django-autocomplete-light
или какие-либо решения JavaScript, один из способов смягчить эту проблему - заменить filter_horizontal на raw_id_fields. Этот виджет не загружает все опции сразу и лучше подходит для больших наборов данных:
class GroupMailIdsAdmin(admin.ModelAdmin):
raw_id_fields = ('mailboxes',)
Это изменение позволит отобразить простое поле ввода с кнопкой поиска для поля «Почтовые ящики», избегая накладных расходов на загрузку всех опций почтового ящика.
Кроме того, если возможно, сократите количество почтовых ящиков, представляемых пользователю. Например, отфильтруйте их по определенным критериям, которые имеют смысл для вашего приложения. Или можно создать собственный виджет, который будет более эффективно обрабатывать большие наборы данных, не прибегая к помощи дополнительных библиотек JavaScript.