12 способов кастомизации Django admin — поиск, фильтры, инлайны, действия, автодополнение, list_editable и оптимизация запросов — которые значительно повышают продуктивность.

Я обожаю функции-бумеранги: сделал работу один раз — и они продолжают приносить тебе пользу. Административная панель Django просто набита ими. Небольшие, точечные настройки, которые сбривают минуты с каждой задачи, пока ты не замечаешь, что к пятнице появилось свободное место. Вот 12 изменений, которые стабильно будут помогать вам экономить время, каждую неделю.

Базовая модель
В качестве примера представьте:

# shop/models.py
class Brand(models.Model):
    name = models.CharField(max_length=120)

class Product(models.Model):
    brand = models.ForeignKey(Brand, on_delete=models.PROTECT)
    name = models.CharField(max_length=200)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)

1) Действительно работающий поиск.
Сделай поле с поиском с помощью search_fields действительно рабочим. Так же с читабельным заголовком.

# shop/admin.py
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    search_fields = ["name", "brand__name"]  # Поиск
    search_help_text = "Search by product or brand" # Заголовок

Почему это экономит время? Больше не нужно искать вручную объект. Вбиваете название поля и ищите его.

2) Показать важное в списке — list_display с сортируемыми методами
Выводите поля, о которых вас часто спрашивают — цена, активность, бренд — и сохраняйте возможность их сортировки.

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ("name", "brand_name", "price", "is_active", "created_at")
    ordering = ("-created_at",)

    def brand_name(self, obj):
        return obj.brand.name
    brand_name.admin_order_field = "brand__name"  # включаем сортировку
    brand_name.short_description = "Brand"

Почему это экономит время? Ноль кликов! Больше не нужно переходить на страницу объекта, дабы узнать очевидную информацию о нем.

3) Фильтры, которыми вы будете пользоваться на самом деле (включая кастомные)
Быстро отбирайте по бренду и актуальности; добавьте собственный фильтр «Активно/Неактивно», который отражает бизнес-логику.

from django.contrib.admin import SimpleListFilter

class ActiveFilter(SimpleListFilter):
    title = "status"
    parameter_name = "status"

    def lookups(self, request, model_admin):
        return [("active", "Active"), ("inactive", "Inactive")]

    def queryset(self, request, qs):
        if self.value() == "active":
            return qs.filter(is_active=True)
        if self.value() == "inactive":
            return qs.filter(is_active=False)
        return qs

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_filter = ("brand", "created_at", ActiveFilter)  
    date_hierarchy = "created_at"

Почему это экономит время? Фильтрация по активным, неактивным. Больше никто не тратит время на поиск активных или неактивных объектов.

4) Автодополнение вместо выпадающих списков на 5000 строк
Предотвращайте зависание страницы, вызванное огромными выпадающими списками для внешних ключей.

# shop/admin.py
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    autocomplete_fields = ["brand"]  # Поле "бренд" теперь с автодополнением

@admin.register(Brand)
class BrandAdmin(admin.ModelAdmin):
    search_fields = ["name"]  # обязательно для работы автодополнения

Почему это экономит время? Печатать удобнее, чем выбирать. Можно выбрать случайно не тот. Так же спасает от зависания, когда этих полей может быть тысячи.

5) Устраните N+1 проблему в списках изменений (changelists)
Страницы со списками объектов печально известны лавиной лишних запросов. Две строчки кода решают большую часть проблем.

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_select_related = ("brand",)

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        return qs.select_related("brand")  # Наш спаситель

Почему это экономит время? Страницы загружаются быстро, благодаря select_related. Сотрудникам больше не приходится ждать, пока появятся новые данные.

6) Массовое редактирование прямо в списке — list_editable
Изменяйте булевы флаги или корректируйте цены, не открывая страницы отдельных объектов.

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ("name", "brand_name", "price", "is_active")
    list_editable = ("price", "is_active")
    list_display_links = ("name",)  

Почему это экономит время? Небольшие изменения в 20 элементах занимают секунды, а не 20 загрузок страниц. Меньшая нагрузка на сервер, дьявол кроется в мелочах.

7) Действия в админке, которые соответствуют реальным рабочим процессам
Два основных примера: «Опубликовать/Снять с публикации» и «Экспорт в CSV». Создавайте идемпотентные и информативные действия.

import csv
from django.http import HttpResponse

@admin.action(description="Пометить выбранные как активные")
def mark_active(modeladmin, request, queryset):
    updated = queryset.update(is_active=True)
    modeladmin.message_user(request, f"Активировано {updated} товаров.")

@admin.action(description="Экспорт выбранных в CSV")
def export_csv(modeladmin, request, queryset):
    resp = HttpResponse(content_type="text/csv")
    resp["Content-Disposition"] = "attachment; filename=products.csv"
    writer = csv.writer(resp)
    writer.writerow(["id", "name", "brand", "price", "is_active"])
    for p in queryset.select_related("brand"):
        writer.writerow([p.id, p.name, p.brand.name, p.price, p.is_active])
    return resp

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    actions = [mark_active, export_csv]
    actions_on_top = True
    actions_on_bottom = False

Почему это экономит время? Все делается в один клик

8) Редактирование связанных данных прямо в форме
Работайте с дочерними элементами, не покидая страницу.

class Image(models.Model):
    product = models.ForeignKey(
      Product, on_delete=models.CASCADE, 
      related_name="images")
    
    url = models.URLField()
    is_primary = models.BooleanField(default=False)
class ImageInline(admin.TabularInline):
    model = Image
    extra = 0
    fields = ("url", "is_primary")
    classes = ("collapse",)

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    inlines = [ImageInline]

Почему это экономит время? На одном экране отображается весь блок работы.

8) Редактирование связанных данных прямо в форме
Работайте с дочерними элементами, не покидая страницу.

from django.db import models
from django.forms import TextInput, NumberInput

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.CharField: {"widget": TextInput(attrs={"size": 60})},
        models.DecimalField: {"widget": NumberInput(attrs={"step": "0.01"})},
    }

Почему это экономит время? Меньше ошибочных нажатий и правок. Форма "кажется правильной" для данных.

9) Улучшенные виджеты и значения по умолчанию через formfield_overrides
Понятные формы способствуют внесению точных данных.

from django.db import models
from django.forms import TextInput, NumberInput

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.CharField: {"widget": TextInput(attrs={"size": 60})},
        models.DecimalField: {"widget": NumberInput(attrs={"step": "0.01"})},
    }

Почему это экономит время? Меньше ошибочных нажатий и правок. Форма "кажется правильной" для данных.

10) Группировка полей: fieldsetsreadonly_fields и скрываемые секции
Упрощайте интерфейс для сотрудников, сохраняя доступ к чувствительным данным только для просмотра.

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    readonly_fields = ("created_at",)
    fieldsets = (
        ("Basics", {"fields": ("name", "brand", "price", "is_active")}),
        ("System", {"fields": ("created_at",), "classes": ("collapse",)}),
    )

Почему это экономит время? Редакторы сосредотачиваются на том, что они могут изменить. Системные поля доступны только тогда, когда они нужны, и не мешают в остальное время.

11) Пагинация и сохранение с учетом контекста
Сохраняйте место при переходе по страницам и выходе из них.

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_per_page = 50
    preserve_filters = True
    save_on_top = True

Почему это экономит время? Вы не теряете свои фильтры после сохранения объекта и не прокручиваете страницу слишком часто.

12) Создайте фирменный стиль администратора, чтобы люди чувствовали себя ":как дома".
Небольшие возможности имеют значение.

from django.contrib import admin
admin.site.site_header = "Shop Admin"
admin.site.site_title = "Shop Admin"
admin.site.index_title = "Operations Console"

Почему это экономит время? Новые члены команды быстрее ориентируются в админке. Меньше вопросов по типу "где я?".

Небольшая инструкция по созданию быстрого списка объектов (Changelist)

  • Отображение: показывайте то, что нужно сотрудникам (list_display), и обеспечивайте сортировку.

  • Поиск: используйте search_fields + стандартные фильтры (+ кастомные SimpleListFilter).

  • Скорость: select_related/prefetch_related, ограничение столбцов, list_per_page.

  • Действия: list_editable для мелких правок, actions — для масштабных операций.

  • Сохранение контекста: preserve_filters чтобы сотрудники возвращались к тому же срезу данных.

Мини-кейс: Сокращение времени работы с каталогом с 40 минут до 8

Раньше у нас была еженедельная рутина: обновить цены для товаров со статусом "неактивен, но скоро пополним", опубликовать 20 товаров, выгрузить отчёт для отдела закупок. 

После внедрения улучшений:

  • Кастомный фильтр по статусу и поиск по бренду мгновенно сужали список до нужной группы товаров.

  • list_editable позволял переключить 20 флагов is_active одним действием.

  • Кастомное действие «Экспорт в CSV» заменило ручное создание таблицы.

  • select_related("brand") обеспечил мгрененную загрузку страницы с 1500 товарами.

Теперь задача выполняется одним человеком за восемь минут. Остальные перестали избегать админки.

Заметки о тестировании и безопасности

  • Действия должны быть идемпотентными и сообщать, сколько строк было изменено.

  • Предпочитайте массовые обновления (queryset.update)
    циклам в действиях.

  • Выносите тяжелые операции в фоновые задачи — действия могут ставить задачу в очередь и возвращать пользователю сообщение о статусе.

  • Для диагностики производительности используйте Django Debug Toolbar: ищите лишние запросы, вызванные методами в list_display или встроенными формами (inlines).

Итог

Админка Django - это не побочный проект, а место, где происходит большая часть вашей бизнес-логики. Когда вы настраиваете поиск, фильтры и массовые операции - и устраняете медленные запросы - вы создаёте кумулятивный эффект. Десять минут сэкономлено сегодня, десять завтра - и через неделю это уже сэкономленный час.

Комментарии (0)