Django Admin¶
У Кроці 1 ти вже реєстрував
NoteAdminпростою командою@admin.register(Note). Тут ми розширимо його до повноцінного адмін-класу з кастомними колонками, пошуком, фільтрами і computed-полями.
Що вже є після Кроку 1¶
hello_app/admin.py після Кроку 1:
from django.contrib import admin
from .models import Note
@admin.register(Note)
class NoteAdmin(admin.ModelAdmin):
list_display = ('title', 'created_at')
search_fields = ('title', 'content')
list_filter = ('created_at',)
ordering = ('-created_at',)
readonly_fields = ('created_at',)
Це вже непогано. Але розглянемо кожну опцію окремо.
Розширений NoteAdmin¶
from django.contrib import admin
from django.utils.html import format_html
from .models import Note
@admin.register(Note)
class NoteAdmin(admin.ModelAdmin):
# ─── Список ────────────────────────────────────────────────────────────────
list_display = ['title', 'short_content', 'created_at']
# Колонки у таблиці-списку: назва, preview тексту, дата
# Перша колонка завжди посилання на деталь
search_fields = ['^title', 'content']
# '^title' → LIKE 'search%' (швидше) vs 'title' → LIKE '%search%'
# 'content' → повнотекстовий пошук
list_filter = ['created_at']
# Фільтри праворуч: "Будь-яка дата", "Сьогодні", "Цього тижня"...
ordering = ['-created_at']
# Сортування: нові першими (Override ordering з Meta)
list_per_page = 20
# Пагінація: 20 записів на сторінку
# ─── Деталь ─────────────────────────────────────────────────────────────────
readonly_fields = ['created_at']
# Ці поля показуються, але не редагуються (auto_now_add → вже editable=False)
# ─── Computed колонка ────────────────────────────────────────────────────────
@admin.display(description='Зміст (preview)')
def short_content(self, obj):
"""Показує перші 80 символів content у списку."""
if not obj.content:
return '—'
preview = obj.content[:80] + ('...' if len(obj.content) > 80 else '')
return format_html('<span style="color:#6b7280">{}</span>', preview)
Анатомія ModelAdmin¶
list_display¶
list_display = ['title', 'short_content', 'created_at']
# ↑ Кожен елемент — або поле моделі, або метод класу NoteAdmin, або метод моделі
# Поле моделі:
list_display = ['title', 'created_at']
# Метод NoteAdmin з @admin.display:
@admin.display(description='Зміст (preview)')
def short_content(self, obj):
return obj.content[:80]
# Метод моделі:
# В models.py:
def preview(self):
return self.content[:80]
# В admin.py:
list_display = ['title', 'preview', 'created_at']
search_fields — синтаксис пошуку¶
search_fields = ['^title', 'content', '=status']
# Prefix | SQL LIKE | Коли використовувати
# ──────────────────────────────────────────────────
# (none) | LIKE '%query%' | Повнотекстовий (повільно на великих таблицях)
# '^' | LIKE 'query%' | Пошук з початку (індексується, швидше)
# '=' | = 'query' | Точний збіг
# '@' | Повнотекстовий | MySQL full-text (не SQLite)
list_filter — вбудовані фільтри¶
list_filter = ['created_at', 'is_public']
# DateTimeField → автоматичні фільтри: "Будь-яка дата", "Сьогодні", "Цього тижня"...
# BooleanField → "Всі", "Так", "Ні"
format_html — безпечний HTML в admin¶
from django.utils.html import format_html
@admin.display(description='Статус')
def status_badge(self, obj):
color = 'green' if obj.is_active else 'red'
label = 'Активна' if obj.is_active else 'Архів'
# format_html безпечно вставляє HTML:
# → escaping для всіх {} → захист від XSS
return format_html(
'<span style="color:{};font-weight:bold">{}</span>',
color, # ← escaped
label, # ← escaped
)
# Не конкатенуй рядки! f"<span>{obj.title}</span>" → XSS якщо title має <script>
admin.site.register vs @admin.register¶
# Варіант 1: admin.site.register (старий стиль)
admin.site.register(Note, NoteAdmin)
# Варіант 2: @admin.register (сучасний, декоратор)
@admin.register(Note)
class NoteAdmin(admin.ModelAdmin):
...
# Результат однаковий. @admin.register читабельніший.
Реєстрація кількох моделей¶
@admin.register(Note)
class NoteAdmin(admin.ModelAdmin):
...
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
list_display = ['name', 'slug']
prepopulated_fields = {'slug': ('name',)}
# prepopulated_fields: автоматично заповнює slug з name (через JS)
Де знайти адмін-URL нотатки¶
/admin/hello_app/note/ ← список нотаток
/admin/hello_app/note/add/ ← додати нотатку
/admin/hello_app/note/42/ ← редагувати нотатку з id=42
/admin/hello_app/note/42/delete/ ← видалити
Формат URL: /admin/<app_label>/<model_name>/
app_label — назва з AppConfig.label (за замовчуванням = назва папки додатку).
Що далі з Django Admin¶
На цьому кроці ми використовуємо стандартний django.contrib.admin.ModelAdmin.
У Кроці 4 (Templates і Forms) буде:
- django-unfold — Tailwind-стилізований admin замість стандартного
- UNFOLD config у settings.py (sidebar navigation, icons, branding)
- Ре-реєстрація User і Group через Unfold форми