Подтвердить что ты не робот

Как заставить Django Admin использовать select_related?

Одна из моих моделей особенно сложна. Когда я пытаюсь редактировать его в Django Admin, он выполняет 1042 запроса и занимает более 9 секунд для обработки.

Я знаю, что я могу заменить несколько выпадающих списков на raw_id_fields, но я думаю, что более узким местом является то, что он не выполняет select_related(), как должен.

Могу ли я получить сайт администратора для этого?

4b9b3361

Ответ 1

Хотя ответ dr jimbob имеет смысл, для моих нужд я смог просто переопределить метод get_queryset() с помощью однострочного интерфейса, даже выбрав внешний ключ внешнего ключа. Возможно, это может быть полезно кому-то.

class MyModelAdmin(admin.ModelAdmin):
    model = MyModel
    ...
    def get_queryset(self, request):
        return super(MyModelAdmin, self).get_queryset(request).select_related(
            'foreign_key1', 'foreign_key2__fk2_foreign_key')

Ответ 3

Для моей конкретной модели особенно медленный аспект проходит через ForeignKeys, когда они отображаются в формах, которые не называются с помощью select_related, так что часть, которую я собираюсь ускорить.

Просматривая соответствующий источник django, вы видите в django/contrib/admin/options.py, что метод formfield_for_foreignkeys принимает каждый FK db_field и вызывает метод ForeignKey class formfield, который определен в django/db/models/поля/связанные/как:

def formfield(self, **kwargs):
    db = kwargs.pop('using', None)
    defaults = {
        'form_class': forms.ModelChoiceField,
        'queryset': self.rel.to._default_manager.using(db).complex_filter(self.rel.limit_choices_to),
        'to_field_name': self.rel.field_name,
    }
    defaults.update(kwargs)
    return super(ForeignKey, self).formfield(**defaults)

Из этого мы видим, если мы предоставляем db_field с помощью kwargs['queryset'], мы можем определить пользовательский набор запросов, который будет использоваться select_related (это может быть предоставлено formfield_for_foreignkey).

Итак, в основном мы хотим переопределить admin.ModelAdmin с помощью SelectRelatedModelAdmin, а затем сделать наши подклассы ModelAdmin SelectRelatedModelAdmin вместо admin.ModelAdmin

class SelectRelatedModelAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if 'queryset' in kwargs:
            kwargs['queryset'] = kwargs['queryset'].select_related()
        else:
            db = kwargs.pop('using', None)
            kwargs['queryset'] = db_field.rel.to._default_manager.using(db).complex_filter(db_field.rel.limit_choices_to).select_related()
        return super(SelectRelatedModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

Этот пример кода не охватывает admin Inline или ManyToManyField s, либо обход foreign_key в функциях, вызываемых readonly_fields или пользовательскими select_related запросами, но аналогичный подход должен работать для этих случаев.