Django Admin Aanpassen in 2026: Actions, Filters en Sollicitatievragen
Complete handleiding voor Django Admin aanpassingen met list_display, SimpleListFilter, custom actions en facet counts. Inclusief veelgestelde sollicitatievragen voor Django-ontwikkelaars.

Het aanpassen van Django Admin transformeert de ingebouwde interface van een scaffold-tool naar een doelgericht backoffice voor productieapplicaties. Django 6 bevat verbeterde admin-functies zoals facet counts voor filters, aanpasbare wachtwoordwijzigingsformulieren en Font Awesome iconen, waardoor een goed geconfigureerde admin daadwerkelijk prettig in gebruik is.
ModelAdmin is de centrale klasse voor alle admin-aanpassingen. Het beheersen van list_display, list_filter, search_fields, fieldsets en custom actions dekt 90% van de admin-configuratiebehoeften.
ModelAdmin Basisprincipes: list_display en search_fields
list_display bepaalt welke kolommen verschijnen op de wijzigingslijstpagina. Naast eenvoudige veldnamen accepteert het callables die gedecoreerd zijn met @admin.display() voor berekende waarden. search_fields activeert het zoekveld dat records standaard filtert met 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)De @admin.display() decorator vervangt het oudere patroon van het instellen van short_description als functieattribuut. De parameter boolean=True rendert een vinkje- of X-icoon in plaats van True/False tekst. Het gebruik van list_select_related voorkomt N+1 queries bij het benaderen van foreign key velden in list_display.
Aangepaste list_filter met SimpleListFilter
list_filter activeert zijbalkfilters op de wijzigingslijstpagina. Veldnamen werken voor basis filtering, maar SimpleListFilter behandelt complexe filterlogica die meerdere velden omvat of aangepaste queries vereist.
# 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 beperkt de auteur-dropdown tot auteurs die daadwerkelijk artikelen hebben, in plaats van elke gebruiker in de database te tonen. Dit verbetert de bruikbaarheid wanneer het gerelateerde model veel records heeft.
Django 6 Facetten: Filtertellingen in de Zijbalk
Django 6 introduceerde facet counts die het aantal overeenkomende objecten naast elke filteroptie tonen. Het attribuut show_facets beheert dit gedrag.
# 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)Facet counts voeren extra COUNT queries uit voor elke filteroptie. Op tabellen met miljoenen rijen moet show_facets = ShowFacets.NEVER worden ingesteld om prestatievermindering te voorkomen. De tellingen worden dynamisch bijgewerkt wanneer filters worden toegepast, en tonen hoeveel records overeenkomen met de huidige filtercombinatie.
Aangepaste Admin Actions voor Bulkoperaties
Admin actions voeren bulkoperaties uit op geselecteerde objecten. De @admin.action decorator stelt permissies en de in de dropdown getoonde beschrijving in.
# 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 actionsHet retourneren van een HttpResponse vanuit een action triggert een bestandsdownload of redirect naar een bevestigingspagina. De permissions parameter beperkt de action tot gebruikers met specifieke modelpermissies. Het overschrijven van get_actions() maakt het mogelijk om actions dynamisch te tonen of verbergen op basis van het huidige verzoek.
Klaar om je Django gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
Fieldsets en Inline Modellen voor Georganiseerde Formulieren
fieldsets organiseert de toevoeg- en wijzigingsformulieren in inklapbare secties. TabularInline en StackedInline embedden gerelateerde modellen direct in het ouderformulier, waardoor navigatie tussen pagina's wordt geëlimineerd.
# 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 toont gerelateerde objecten in een compact tabelformaat, geschikt voor eenvoudige modellen met weinig velden. StackedInline toont elk gerelateerd object als een apart formulierblok, beter voor modellen met veel velden. De optie autocomplete_fields vervangt de standaard dropdown met een AJAX-zoekwidget, essentieel wanneer het gerelateerde model duizenden records heeft.
De Admin Site Aanpassen
Het subclassen van AdminSite maakt meerdere admin-interfaces mogelijk met verschillende branding, permissies of modelregistraties. Django 6 voegde password_change_form aanpassing toe voor strengere wachtwoordbeleid.
# 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),Elke AdminSite instantie heeft zijn eigen URL namespace, login pagina en set geregistreerde modellen. Dit patroon is geschikt voor applicaties met verschillende gebruikersrollen waarbij editors geen gebruikersbeheer of systeeminstellingen zouden moeten zien.
Sollicitatievragen: Django Admin Aanpassing
Deze vragen verschijnen vaak in technische Django-sollicitaties. Het begrijpen van de onderliggende mechanismen, niet alleen de syntax, onderscheidt mid-level kandidaten van senior engineers.
V: Hoe voeg je een berekende kolom toe aan list_display die niet kan worden gesorteerd?
De @admin.display() decorator accepteert een ordering parameter die specificeert welk databaseveld te gebruiken voor sortering. Zonder dit is de kolomkop niet klikbaar. Berekende waarden die meerdere records aggregeren of externe services aanroepen kunnen niet worden gesorteerd omdat er geen onderliggende databasekolom is om op te ordenen.
V: Wat is de prestatie-impact van facet counts en wanneer moeten ze worden uitgeschakeld?
Facets voeren één COUNT query uit per filteroptie, vermenigvuldigd met het aantal actieve filters. Op een tabel met 10 miljoen rijen en 5 filteropties elk op 3 velden, zijn dat 15 extra COUNT queries per paginalading. Stel show_facets = ShowFacets.NEVER in wanneer een filter een tabel met meer dan 100.000 rijen target zonder geschikte indexes.
V: Hoe verschillen admin actions van aangepaste admin views?
Actions werken op een queryset van geselecteerde objecten en integreren met de selectie-checkboxen van de wijzigingslijst. Aangepaste admin views zijn standalone pagina's die toegankelijk zijn via aangepaste URL's, geschikt voor rapporten, imports of dashboards die niet op geselecteerde records werken. Actions zouden geen langlopende operaties moeten uitvoeren omdat ze het verzoek blokkeren.
V: Waarom RelatedOnlyFieldListFilter gebruiken in plaats van het standaardfilter?
Het standaard RelatedFieldListFilter bevraagt alle objecten in de gerelateerde tabel. Wanneer een ForeignKey verwijst naar een User model met 100.000 records, laadt de filter-dropdown alle 100.000 opties, waardoor de browser vastloopt. RelatedOnlyFieldListFilter bevraagt alleen de waarden die bestaan in de records van het huidige model, typisch een veel kleinere set.
Veelgemaakte Fouten Vermijden in Productie
Verschillende patronen die werken in ontwikkeling veroorzaken problemen op schaal.
# 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_totalHet overschrijven van get_queryset() om annotaties toe te voegen houdt berekende waarden in een enkele query in plaats van de database eenmaal per rij te benaderen. Profileer de admin met Django Debug Toolbar om N+1 queries te vangen voordat ze productie bereiken.
Third-Party Admin Thema's: Django Unfold
Django Unfold is het meest actief onderhouden admin-thema voor Django 6, gebouwd met Tailwind CSS. Het breidt de standaard admin uit in plaats van te vervangen, dus bestaande ModelAdmin configuraties blijven werken.
# 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 voegt dark mode, responsive layouts, zijbalknavigatie en geavanceerde filtertypen toe zoals RangeNumericFilter en RangeDateFilter. De HTMX en Alpine.js integratie biedt interactiviteit zonder de overhead van een volledig SPA-framework. Voor teams die een gepolijste admin nodig hebben zonder een aangepast dashboard te bouwen, bespaart Unfold weken frontend-werk.
Begin met oefenen!
Test je kennis met onze gespreksimulatoren en technische tests.
Samenvatting Django Admin Aanpassing
list_displaymet@admin.display()beheert kolommen; voegorderingtoe voor sorteerbare berekende waardenSimpleListFilterbehandelt aangepaste filterlogica die meerdere velden omvat of datumbereiken vereist- Django 6 facets tonen filtertellingen; uitschakelen met
ShowFacets.NEVERop grote tabellen om COUNT query overhead te vermijden - Admin actions met
@admin.action(permissions=[...])behandelen bulkoperaties; retourneerHttpResponsevoor bestandsexports - Gebruik
list_select_relateden geannoteerde querysets inget_queryset()om N+1 queries in lijstweergaven te voorkomen RelatedOnlyFieldListFiltervoorkomt browser-vastlopers bij filteren op ForeignKey naar grote tabellen- Oefen deze patronen met Django sollicitatievragen die specifiek gericht zijn op admin-configuratie
Zie jij de bug in Django?
Een echt codefragment, een verborgen bug, één poging per dag. Zonder account uit te proberen.

Geschreven door
Anthony Fillion-MailletOprichter van SharpSkill
Al meer dan 10 jaar fullstack-ontwikkelaar. Hij leidt SharpSkill en staat in voor alles wat hier verschijnt.
Bijgewerkt op 13 september 2026
Delen
Gerelateerde artikelen

Geavanceerde Django Middleware in 2026: Custom Middleware, Logging en Sollicitatievragen
Uitgebreide gids over Django Middleware in 2026: request-response cyclus, aangepaste middleware-klassen, async middleware, logging-integratie en veelgestelde technische sollicitatievragen.

Django Signals vs Celery Tasks 2026: Wanneer Wat Gebruiken en Sollicitatievragen
Leer de verschillen tussen Django Signals en Celery Tasks, wanneer welke tool te gebruiken en veelgestelde sollicitatievragen voor 2026.

Django Channels in 2026: WebSockets, Real-Time Communicatie en Sollicitatievragen
Django Channels 4.x tutorial met WebSocket consumers, Redis channel layers en sollicitatievragen voor senior ontwikkelaars.