Personalizzazione Django Admin 2026: Actions, Filtri e Domande da Colloquio
Guida completa alla personalizzazione di Django Admin con list_display, SimpleListFilter, Actions personalizzate e facet counts. Include domande frequenti nei colloqui tecnici Django.

La personalizzazione del Django Admin trasforma l'interfaccia integrata da uno strumento di scaffolding in un back office su misura per applicazioni in produzione. Django 6 include funzionalità admin avanzate come i conteggi facet per i filtri, form personalizzabili per il cambio password e icone Font Awesome, rendendo un admin ben configurato effettivamente piacevole da usare.
ModelAdmin è la classe centrale per tutte le personalizzazioni admin. Padroneggiare list_display, list_filter, search_fields, fieldsets e actions personalizzate copre il 90% delle esigenze di configurazione admin.
Fondamenti di ModelAdmin: list_display e search_fields
list_display controlla quali colonne appaiono nella pagina dell'elenco modifiche. Oltre ai semplici nomi di campo, accetta callable decorati con @admin.display() per valori calcolati. search_fields abilita la casella di ricerca che filtra i record usando icontains di default.
# 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)Il decoratore @admin.display() sostituisce il vecchio pattern di impostare short_description come attributo della funzione. Il parametro boolean=True renderizza un'icona di spunta o X invece del testo True/False. L'uso di list_select_related previene le query N+1 quando si accede ai campi foreign key in list_display.
list_filter Personalizzati con SimpleListFilter
list_filter attiva i filtri nella sidebar nella pagina dell'elenco modifiche. I nomi dei campi funzionano per il filtraggio base, ma SimpleListFilter gestisce logiche di filtraggio complesse che coinvolgono più campi o richiedono query personalizzate.
# 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 limita il dropdown degli autori solo a quelli che hanno effettivamente articoli, invece di elencare ogni utente nel database. Questo migliora l'usabilità quando il modello correlato ha molti record.
Facet di Django 6: Conteggi Filtri nella Sidebar
Django 6 ha introdotto i conteggi facet che mostrano il numero di oggetti corrispondenti accanto a ogni opzione di filtro. L'attributo show_facets controlla questo comportamento.
# 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)I conteggi facet eseguono query COUNT aggiuntive per ogni opzione di filtro. Su tabelle con milioni di righe, impostare show_facets = ShowFacets.NEVER per evitare degradazioni delle prestazioni. I conteggi si aggiornano dinamicamente quando i filtri vengono applicati, mostrando quanti record corrispondono alla combinazione di filtri corrente.
Admin Actions Personalizzate per Operazioni in Blocco
Le Admin Actions eseguono operazioni in blocco sugli oggetti selezionati. Il decoratore @admin.action imposta i permessi e la descrizione mostrata nel dropdown.
# 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 actionsRestituire una HttpResponse da un'action attiva un download di file o reindirizza a una pagina di conferma. Il parametro permissions limita l'action agli utenti con permessi specifici del modello. Sovrascrivere get_actions() permette di mostrare o nascondere dinamicamente le actions in base alla richiesta corrente.
Pronto a superare i tuoi colloqui su Django?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
Fieldsets e Modelli Inline per Form Organizzati
fieldsets organizza i form di aggiunta e modifica in sezioni comprimibili. TabularInline e StackedInline incorporano i modelli correlati direttamente nel form del parent, eliminando la navigazione tra pagine.
# 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 visualizza gli oggetti correlati in un formato tabella compatto, adatto per modelli semplici con pochi campi. StackedInline mostra ogni oggetto correlato come un blocco form separato, migliore per modelli con molti campi. L'opzione autocomplete_fields sostituisce il dropdown predefinito con un widget di ricerca AJAX, essenziale quando il modello correlato ha migliaia di record.
Personalizzazione dell'Admin Site
Creare sottoclassi di AdminSite permette interfacce admin multiple con branding, permessi o registrazioni di modelli diversi. Django 6 ha aggiunto la personalizzazione di password_change_form per policy password più rigide.
# 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),Ogni istanza AdminSite ha il proprio namespace URL, pagina di login e set di modelli registrati. Questo pattern è adatto per applicazioni con ruoli utente distinti dove gli editor non dovrebbero vedere la gestione utenti o le impostazioni di sistema.
Domande da Colloquio: Personalizzazione Django Admin
Queste domande appaiono frequentemente nei colloqui tecnici Django. Comprendere i meccanismi sottostanti, non solo la sintassi, distingue i candidati mid-level dai senior engineer.
D: Come si aggiunge una colonna calcolata a list_display che non può essere ordinata?
Il decoratore @admin.display() accetta un parametro ordering che specifica quale campo del database usare per l'ordinamento. Senza di esso, l'intestazione della colonna non è cliccabile. I valori calcolati che aggregano più record o chiamano servizi esterni non possono essere ordinati perché non c'è una colonna database sottostante su cui ordinare.
D: Qual è l'impatto sulle prestazioni dei conteggi facet e quando dovrebbero essere disabilitati?
I facet eseguono una query COUNT per opzione di filtro, moltiplicata per il numero di filtri attivi. Su una tabella con 10 milioni di righe e 5 opzioni di filtro ciascuna su 3 campi, sono 15 query COUNT aggiuntive per caricamento pagina. Impostare show_facets = ShowFacets.NEVER quando qualsiasi filtro punta a una tabella con oltre 100.000 righe senza indici appropriati.
D: Come differiscono le admin actions dalle view admin personalizzate?
Le actions operano su un queryset di oggetti selezionati e si integrano con le checkbox di selezione dell'elenco modifiche. Le view admin personalizzate sono pagine standalone accessibili tramite URL personalizzati, adatte per report, import o dashboard che non operano su record selezionati. Le actions non dovrebbero eseguire operazioni di lunga durata perché bloccano la richiesta.
D: Perché usare RelatedOnlyFieldListFilter invece del filtro predefinito?
Il RelatedFieldListFilter predefinito interroga tutti gli oggetti nella tabella correlata. Quando una ForeignKey punta a un modello User con 100.000 record, il dropdown del filtro carica tutte le 100.000 opzioni, bloccando il browser. RelatedOnlyFieldListFilter interroga solo i valori che esistono nei record del modello corrente, tipicamente un set molto più piccolo.
Errori Comuni da Evitare in Produzione
Diversi pattern che funzionano in sviluppo causano problemi a scala.
# 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_totalSovrascrivere get_queryset() per aggiungere annotazioni mantiene i valori calcolati in una singola query invece di interrogare il database una volta per riga. Profilare l'admin con Django Debug Toolbar per individuare le query N+1 prima che raggiungano la produzione.
Temi Admin di Terze Parti: Django Unfold
Django Unfold è il tema admin più attivamente mantenuto per Django 6, costruito con Tailwind CSS. Estende piuttosto che sostituire l'admin standard, quindi le configurazioni ModelAdmin esistenti continuano a funzionare.
# 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 aggiunge dark mode, layout responsive, navigazione sidebar e tipi di filtro avanzati come RangeNumericFilter e RangeDateFilter. L'integrazione HTMX e Alpine.js fornisce interattività senza l'overhead di un framework SPA completo. Per i team che necessitano di un admin curato senza costruire una dashboard personalizzata, Unfold risparmia settimane di lavoro frontend.
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
Riepilogo sulla Personalizzazione Django Admin
list_displaycon@admin.display()controlla le colonne; aggiungereorderingper valori calcolati ordinabiliSimpleListFiltergestisce logiche di filtro personalizzate che coinvolgono più campi o richiedono intervalli di date- I facet di Django 6 mostrano i conteggi dei filtri; disabilitare con
ShowFacets.NEVERsu tabelle grandi per evitare l'overhead delle query COUNT - Admin actions con
@admin.action(permissions=[...])gestiscono operazioni in blocco; restituireHttpResponseper export di file - Usare
list_select_relatede queryset annotati inget_queryset()per prevenire query N+1 nelle view elenco RelatedOnlyFieldListFilterpreviene il blocco del browser quando si filtra per ForeignKey verso tabelle grandi- Esercitarsi con questi pattern usando le domande da colloquio Django che mirano specificamente alla configurazione admin
Sapresti trovare il bug in Django?
Uno snippet reale, un bug nascosto, un tentativo al giorno. Senza account per provare.

Scritto da
Anthony Fillion-MailletFondatore di SharpSkill
Sviluppatore fullstack da oltre 10 anni. Guida SharpSkill e risponde di tutto ciò che vi viene pubblicato.
Aggiornato il 13 settembre 2026
Condividi
Articoli correlati

Django Middleware Avanzato nel 2026: Middleware Personalizzato, Logging e Domande da Colloquio
Guida completa al Django Middleware nel 2026: ciclo request-response, classi middleware personalizzate, middleware asincrono, integrazione logging e domande tecniche frequenti nei colloqui.

Django Signals vs Celery Tasks 2026: Quando Usare Quale e Domande per Colloqui
Scopri le differenze tra Django Signals e Celery Tasks, quando utilizzare ciascuno strumento e le domande più frequenti nei colloqui tecnici 2026.

Django Channels nel 2026: WebSocket, Comunicazione Real-Time e Domande da Colloquio
Tutorial Django Channels 4.x con WebSocket consumer, Redis channel layer e domande da colloquio per sviluppatori senior.