Personalizacja Panelu Administracyjnego Django w 2026: Akcje, Filtry i Pytania Rekrutacyjne
Kompletny przewodnik po personalizacji panelu administracyjnego Django 6 z niestandardowymi akcjami, filtrami, fieldsetami i inline. Zawiera pytania rekrutacyjne.

Personalizacja panelu administracyjnego Django przekształca wbudowany interfejs z prostego narzędzia do zarządzania danymi w profesjonalne zaplecze dla aplikacji produkcyjnych. Django 6 wprowadza ulepszone funkcje administracyjne, w tym liczniki facetów dla filtrów, konfigurowalne formularze zmiany hasła oraz ikony Font Awesome, dzięki czemu dobrze skonfigurowany panel administracyjny staje się przyjemny w użyciu.
ModelAdmin to centralna klasa dla całej personalizacji panelu administracyjnego. Opanowanie list_display, list_filter, search_fields, fieldsets oraz niestandardowych akcji pozwala obsłużyć 90% potrzeb konfiguracyjnych panelu.
Podstawy ModelAdmin: list_display i search_fields
list_display kontroluje, które kolumny pojawiają się na stronie listy zmian. Oprócz prostych nazw pól, akceptuje funkcje wywoływalne opatrzone dekoratorem @admin.display() dla wartości obliczanych. search_fields włącza pole wyszukiwania, które domyślnie filtruje rekordy przy użyciu icontains.
# admin.py
from django.contrib import admin
from django.utils.html import format_html
from .models import Article
class ArticleAdmin(admin.ModelAdmin):
list_display = ["title", "author_name", "status", "publish_date", "is_recent"]
search_fields = ["title", "author__email", "content"]
list_per_page = 50
list_select_related = ["author"] # Avoid N+1 queries
@admin.display(description="Author", ordering="author__last_name")
def author_name(self, obj):
return f"{obj.author.first_name} {obj.author.last_name}"
@admin.display(description="Recent?", boolean=True)
def is_recent(self, obj):
from django.utils import timezone
return obj.publish_date >= timezone.now() - timezone.timedelta(days=7)
admin.site.register(Article, ArticleAdmin)Dekorator @admin.display() zastępuje starszy wzorzec ustawiania short_description jako atrybutu funkcji. Parametr boolean=True renderuje ikonę znacznika lub X zamiast tekstu True/False. Użycie list_select_related zapobiega zapytaniom N+1 podczas dostępu do pól kluczy obcych w list_display.
Niestandardowe list_filter z SimpleListFilter
list_filter aktywuje filtry na pasku bocznym strony listy zmian. Nazwy pól działają dla podstawowego filtrowania, ale SimpleListFilter obsługuje złożoną logikę filtrowania obejmującą wiele pól lub wymagającą niestandardowych zapytań.
# admin.py
from datetime import date, timedelta
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from .models import Article
class PublishDateFilter(admin.SimpleListFilter):
title = _("publish period")
parameter_name = "published" # URL parameter: ?published=this_week
def lookups(self, request, model_admin):
# Returns (value, label) tuples for the filter sidebar
return [
("this_week", _("This week")),
("this_month", _("This month")),
("this_year", _("This year")),
("unpublished", _("Not published")),
]
def queryset(self, request, queryset):
# Filter the queryset based on self.value()
today = date.today()
if self.value() == "this_week":
return queryset.filter(publish_date__gte=today - timedelta(days=7))
if self.value() == "this_month":
return queryset.filter(
publish_date__year=today.year, publish_date__month=today.month
)
if self.value() == "this_year":
return queryset.filter(publish_date__year=today.year)
if self.value() == "unpublished":
return queryset.filter(publish_date__isnull=True)
return queryset
class ArticleAdmin(admin.ModelAdmin):
list_filter = [
"status",
PublishDateFilter,
("author", admin.RelatedOnlyFieldListFilter), # Show only authors with articles
]RelatedOnlyFieldListFilter ogranicza listę rozwijaną autorów tylko do tych, którzy faktycznie mają artykuły, zamiast wyświetlać każdego użytkownika w bazie danych. To poprawia użyteczność, gdy powiązany model zawiera wiele rekordów.
Facety w Django 6: Liczniki Filtrów na Pasku Bocznym
Django 6 wprowadziło liczniki facetów, które wyświetlają liczbę pasujących obiektów obok każdej opcji filtra. Atrybut show_facets kontroluje to zachowanie.
# admin.py
from django.contrib import admin
from django.contrib.admin import ShowFacets
from .models import Article
class ArticleAdmin(admin.ModelAdmin):
list_display = ["title", "status", "category"]
list_filter = ["status", "category", "author"]
show_facets = ShowFacets.ALWAYS # Always display counts
# Alternative values:
# ShowFacets.ALLOW - Show counts when ?_facets=1 in URL
# ShowFacets.NEVER - Never show counts (for large datasets)Liczniki facetów wykonują dodatkowe zapytania COUNT dla każdej opcji filtra. Na tabelach z milionami wierszy należy ustawić show_facets = ShowFacets.NEVER, aby uniknąć degradacji wydajności. Liczniki aktualizują się dynamicznie w miarę stosowania filtrów, pokazując ile rekordów pasuje do bieżącej kombinacji filtrów.
Niestandardowe Akcje Administracyjne dla Operacji Zbiorczych
Akcje administracyjne wykonują operacje zbiorcze na wybranych obiektach. Dekorator @admin.action ustawia uprawnienia oraz opis wyświetlany w menu rozwijanym.
# admin.py
from django.contrib import admin
from django.contrib import messages
from django.utils.translation import ngettext
from .models import Article
@admin.action(description="Publish selected articles", permissions=["change"])
def publish_articles(modeladmin, request, queryset):
# Exclude already published articles
unpublished = queryset.exclude(status="published")
updated = unpublished.update(status="published")
modeladmin.message_user(
request,
ngettext(
"%d article was published.",
"%d articles were published.",
updated,
)
% updated,
messages.SUCCESS,
)
@admin.action(description="Export selected as JSON")
def export_as_json(modeladmin, request, queryset):
from django.http import HttpResponse
from django.core import serializers
response = HttpResponse(content_type="application/json")
response["Content-Disposition"] = 'attachment; filename="articles.json"'
serializers.serialize("json", queryset, stream=response)
return response # Returning HttpResponse triggers file download
class ArticleAdmin(admin.ModelAdmin):
actions = [publish_articles, export_as_json]
def get_actions(self, request):
# Conditionally remove actions based on user permissions
actions = super().get_actions(request)
if not request.user.has_perm("articles.delete_article"):
del actions["delete_selected"]
return actionsZwrócenie HttpResponse z akcji uruchamia pobieranie pliku lub przekierowuje na stronę potwierdzenia. Parametr permissions ogranicza akcję do użytkowników z określonymi uprawnieniami modelu. Nadpisanie get_actions() pozwala dynamicznie pokazywać lub ukrywać akcje na podstawie bieżącego żądania.
Gotowy na rozmowy o Django?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Fieldsets i Modele Inline dla Zorganizowanych Formularzy
fieldsets organizuje formularze dodawania i edycji w zwijane sekcje. TabularInline i StackedInline osadzają powiązane modele bezpośrednio w formularzu nadrzędnym, eliminując nawigację między stronami.
# admin.py
from django.contrib import admin
from .models import Article, ArticleImage, ArticleComment
class ArticleImageInline(admin.TabularInline):
model = ArticleImage
extra = 1 # Number of empty forms to display
max_num = 5 # Maximum images per article
fields = ["image", "caption", "order"]
class ArticleCommentInline(admin.StackedInline):
model = ArticleComment
extra = 0
readonly_fields = ["created_at", "author"]
can_delete = True
class ArticleAdmin(admin.ModelAdmin):
fieldsets = [
(
None,
{
"fields": ["title", "slug", "author"],
},
),
(
"Content",
{
"fields": ["content", "excerpt"],
"classes": ["wide"], # Full-width fields
},
),
(
"Publishing",
{
"fields": ["status", "publish_date", "category", "tags"],
"classes": ["collapse"], # Collapsed by default
},
),
(
"SEO",
{
"fields": ["meta_title", "meta_description"],
"classes": ["collapse"],
"description": "Search engine optimization fields",
},
),
]
inlines = [ArticleImageInline, ArticleCommentInline]
prepopulated_fields = {"slug": ["title"]} # Auto-generate slug from title
autocomplete_fields = ["author", "category"] # AJAX-powered select
filter_horizontal = ["tags"] # Widget for M2M selection
admin.site.register(Article, ArticleAdmin)TabularInline wyświetla powiązane obiekty w kompaktowym formacie tabeli, odpowiednim dla prostych modeli z kilkoma polami. StackedInline pokazuje każdy powiązany obiekt jako oddzielny blok formularza, lepszy dla modeli z wieloma polami. Opcja autocomplete_fields zastępuje domyślną listę rozwijaną widżetem wyszukiwania AJAX, niezbędnym gdy powiązany model zawiera tysiące rekordów.
Personalizacja Witryny Administracyjnej
Podklasowanie AdminSite umożliwia tworzenie wielu interfejsów administracyjnych z różnym brandingiem, uprawnieniami lub rejestracjami modeli. Django 6 dodało możliwość personalizacji password_change_form dla bardziej rygorystycznych polityk haseł.
# admin.py
from django.contrib import admin
from django.contrib.admin import AdminSite
from .models import Article, Category
class ContentAdminSite(AdminSite):
site_header = "Content Management"
site_title = "CMS"
index_title = "Dashboard"
def has_permission(self, request):
# Restrict to staff in the 'editors' group
return (
request.user.is_active
and request.user.is_staff
and request.user.groups.filter(name="editors").exists()
)
content_admin = ContentAdminSite(name="content_admin")
content_admin.register(Article, ArticleAdmin)
content_admin.register(Category)
# In urls.py:
# path('content-admin/', content_admin.urls),Każda instancja AdminSite ma własną przestrzeń nazw URL, stronę logowania oraz zestaw zarejestrowanych modeli. Ten wzorzec pasuje do aplikacji z odrębnymi rolami użytkowników, gdzie redaktorzy nie powinni widzieć zarządzania użytkownikami ani ustawień systemowych.
Pytania Rekrutacyjne: Personalizacja Panelu Admin Django
Te pytania często pojawiają się na technicznych rozmowach kwalifikacyjnych Django. Zrozumienie mechanizmów działania, a nie tylko składni, odróżnia kandydatów średniego szczebla od starszych inżynierów.
P: Jak dodać kolumnę obliczaną do list_display, która nie może być sortowana?
Dekorator @admin.display() przyjmuje parametr ordering, który określa które pole bazy danych użyć do sortowania. Bez niego nagłówek kolumny nie jest klikalny. Wartości obliczane agregujące wiele rekordów lub wywołujące usługi zewnętrzne nie mogą być sortowane, ponieważ nie ma bazowej kolumny bazy danych do uporządkowania.
P: Jaki jest wpływ liczników facetów na wydajność i kiedy należy je wyłączyć?
Facety wykonują jedno zapytanie COUNT na opcję filtra, pomnożone przez liczbę aktywnych filtrów. Na tabeli z 10 milionami wierszy i 5 opcjami filtra na 3 polach, to 15 dodatkowych zapytań COUNT na każde załadowanie strony. Należy ustawić show_facets = ShowFacets.NEVER gdy jakikolwiek filtr dotyczy tabeli powyżej 100 000 wierszy bez odpowiednich indeksów.
P: Czym różnią się akcje administracyjne od niestandardowych widoków administratora?
Akcje operują na queryset wybranych obiektów i integrują się z polami wyboru listy zmian. Niestandardowe widoki administratora to samodzielne strony dostępne przez niestandardowe adresy URL, odpowiednie dla raportów, importów lub dashboardów, które nie operują na wybranych rekordach. Akcje nie powinny wykonywać długotrwałych operacji, ponieważ blokują żądanie.
P: Dlaczego używać RelatedOnlyFieldListFilter zamiast domyślnego filtra?
Domyślny RelatedFieldListFilter odpytuje wszystkie obiekty w powiązanej tabeli. Gdy klucz obcy wskazuje na model User ze 100 000 rekordami, rozwijana lista filtra ładuje wszystkie 100 000 opcji, zamrażając przeglądarkę. RelatedOnlyFieldListFilter odpytuje tylko wartości istniejące w rekordach bieżącego modelu, zazwyczaj znacznie mniejszy zbiór.
Typowe Błędy do Unikania w Produkcji
Kilka wzorców działających w środowisku deweloperskim powoduje problemy na skalę.
# admin.py
from django.contrib import admin
from .models import Order
class OrderAdmin(admin.ModelAdmin):
# Mistake: Accessing related fields without list_select_related
# This causes N+1 queries, one per row in the list view
list_display = ["id", "customer_email", "total"]
# Fix: Prefetch related objects
list_select_related = ["customer"]
def customer_email(self, obj):
return obj.customer.email
# Mistake: search_fields on unindexed large text columns
# search_fields = ["notes"] # Slow on large tables
# Fix: Limit search to indexed fields
search_fields = ["id", "customer__email"]
# Mistake: Expensive computation in list_display
# def order_total(self, obj):
# return sum(item.price * item.qty for item in obj.items.all()) # N+1
# Fix: Use annotation in get_queryset
def get_queryset(self, request):
from django.db.models import Sum, F
qs = super().get_queryset(request)
return qs.annotate(computed_total=Sum(F("items__price") * F("items__qty")))
@admin.display(description="Total", ordering="computed_total")
def total(self, obj):
return obj.computed_totalNadpisanie get_queryset() w celu dodania adnotacji utrzymuje obliczane wartości w jednym zapytaniu, zamiast odpytywać bazę danych raz na wiersz. Profilowanie panelu administracyjnego za pomocą Django Debug Toolbar pozwala wychwycić zapytania N+1 zanim trafią do produkcji.
Motywy Zewnętrzne dla Panelu Admin: Django Unfold
Django Unfold to najaktywniej rozwijany motyw administracyjny dla Django 6, zbudowany z Tailwind CSS. Rozszerza standardowy panel administracyjny zamiast go zastępować, więc istniejące konfiguracje ModelAdmin nadal działają.
# settings.py
INSTALLED_APPS = [
"unfold", # Must come before django.contrib.admin
"unfold.contrib.filters", # Advanced filter widgets
"unfold.contrib.forms", # Enhanced form widgets
"django.contrib.admin",
# ...
]Unfold dodaje tryb ciemny, responsywne układy, nawigację boczną oraz zaawansowane typy filtrów jak RangeNumericFilter i RangeDateFilter. Integracja z HTMX i Alpine.js zapewnia interaktywność bez obciążenia pełnego frameworka SPA. Dla zespołów potrzebujących dopracowanego panelu administracyjnego bez budowania niestandardowego dashboardu, Unfold oszczędza tygodnie pracy frontendowej.
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Co Zapamiętać o Personalizacji Panelu Admin Django
list_displayz@admin.display()kontroluje kolumny; dodanieorderingumożliwia sortowanie wartości obliczanychSimpleListFilterobsługuje niestandardową logikę filtrowania obejmującą wiele pól lub wymagającą zakresów dat- Facety Django 6 pokazują liczniki filtrów; wyłączenie za pomocą
ShowFacets.NEVERna dużych tabelach pozwala uniknąć narzutu zapytań COUNT - Akcje administracyjne z
@admin.action(permissions=[...])obsługują operacje zbiorcze; zwrócenieHttpResponseumożliwia eksport plików - Użycie
list_select_relatedi adnotowanych queryset wget_queryset()zapobiega zapytaniom N+1 w widokach list RelatedOnlyFieldListFilterzapobiega zamrażaniu przeglądarki podczas filtrowania po kluczu obcym do dużych tabel- Praktykowanie tych wzorców z pytaniami rekrutacyjnymi Django ukierunkowanymi na konfigurację panelu administracyjnego
Znajdziesz błąd w Django?
Prawdziwy fragment kodu, ukryty błąd, jedna próba dziennie. Bez konta, żeby spróbować.

Autor:
Anthony Fillion-MailletZałożyciel SharpSkill
Programista fullstack od ponad 10 lat. Prowadzi SharpSkill i odpowiada za wszystko, co się tu ukazuje.
Zaktualizowano 13 września 2026
Udostępnij
Powiązane artykuły

Zaawansowane Middleware w Django 2026: Tworzenie Własnych Middleware, Logowanie i Pytania Rekrutacyjne
Kompleksowy przewodnik po zaawansowanych technikach middleware w Django 2026. Dowiedz się, jak tworzyć własne middleware, implementować logowanie i przygotować się do rozmów kwalifikacyjnych.

Django Signals vs Celery Tasks w 2026: Kiedy Stosować i Pytania Rekrutacyjne
Kompleksowe porównanie Django Signals i Celery Tasks w 2026 roku. Kiedy używać sygnałów, a kiedy zadań asynchronicznych, z przykładami kodu i pytaniami na rozmowę kwalifikacyjną.

Django Channels w 2026: WebSockety, Komunikacja w Czasie Rzeczywistym i Pytania Rekrutacyjne
Django Channels 4.x rozszerza możliwości Django poza protokół HTTP, umożliwiając obsługę WebSocketów i implementację funkcji czasu rzeczywistego takich jak czat, powiadomienia czy dynamiczne dashboardy.