Как устроена админка в Django и FastAPI
Что проверяют
Знаете ли вы, что Django даёт админку из коробки, а FastAPI нет, и понимаете ли, как в обоих случаях её настроить и защитить. Следом часто спрашивают, почему админка тормозит на большой таблице и как ограничить сотрудникам доступ к части моделей.
Как отвечать
Django admin это приложение django.contrib.admin, которое строит интерфейс по метаданным моделей (Model._meta) и ModelForm. Модель регистрируется через admin.site.register или декоратор @admin.register, поведение задаёт класс ModelAdmin:
- список:
list_display,list_filter,search_fields,ordering,list_per_page; - форма:
fieldsилиfieldsets,readonly_fields, связанные записи черезTabularInlineиStackedInline; - производительность:
list_select_relatedпротив N+1 на внешних ключах,autocomplete_fieldsилиraw_id_fieldsвместо выпадающего списка на сто тысяч строк,show_full_result_count = False, чтобы не считатьCOUNT(*)по всей таблице; - логика: переопределение
get_querysetиsave_model, своиactionsдля массовых операций; - права: вход только для
is_staff, дальше права add, change, delete и view на каждую модель и группы. История изменений пишется вLogEntry.
Встроенной админки у FastAPI нет. Готовые варианты: SQLAdmin для SQLAlchemy (класс ModelView с column_list и column_searchable_list, подключение через admin.add_view), Starlette-Admin, FastAPI-Admin для Tortoise ORM. Вход в SQLAdmin вы реализуете сами через AuthenticationBackend. Второй путь: отдельный фронтенд вроде React Admin поверх вашего API.
Не путайте админку с /docs: Swagger UI это документация API, а не панель управления данными.
Где спотыкаются
Называют Tortoise ORM библиотекой для админки. Это ORM, админку к нему даёт отдельный пакет.
Оставляют админку на /admin/ открытой в проде. Её прячут за VPN или список адресов, включают двухфакторный вход и не раздают is_superuser всем сотрудникам.
Не замечают N+1 в list_display. Метод, который обращается к связанной модели, на странице из ста строк делает сто лишних запросов, пока не добавлен list_select_related или select_related в get_queryset.