16. Customizing the Django Admin#
Django’s automatically generated admin
site is one of the biggest strengths of the framework.
In this Chapter, we will discuss about
Perform basic Django admin site configuration
Explain how Django model attributes affect the admin site
Use
list_display
to control which model fields are displayedAdd custom fields to
list_display
and format existing onesAdd links to related model objects in
list_display
Enable search and filters via
search_fields
andlist_filter
Handle model inlines for both
N:1
andM:M
relationshipsUse Django admin actions and create custom ones
Override Django admin forms and templates
Utilize DjangoQL for advanced searching functionality
Import data in and export data to different formats using
django-import-export
Modify the appearance of your admin site via
django-admin-interface
16.1. Basic Admin Site Customization#
The
Django admin site
provides some basic configuration options. These options allow you to change the site’s title, header, site URL, and more.The
admin.site
settings are usually modified in your project’s mainurls.py
file.# django_project/urls.py ... admin.site.site_title = "Blog Site Admin (DEV)" admin.site.site_header = "Blog Administration" admin.site.index_title = "Site Administration" ...
Another thing you should do is change the
default /admin
URL. This’ll make it more difficult for malicious actors to find your admin panel.# django_project/urls.py urlpatterns = [ path("secretadmin/", admin.site.urls), path('users/', include('users.urls')), path('', include('blog.urls')), ] ...
Your admin site should now be accessible at
http://localhost:8000/secretadmin
.
16.2. Django Model and Admin#
Some Django model attributes
directly affect the Django admin site
. Most importantly:
__str__()
is used to define object’s display nameMeta
class is used to set various metadata options (e.g.,ordering
andverbose_name
)
Here’s an example of how these attributes are used in practice:
By providing the
ordering
attribute the categories are now ordered bydate_posted
.class Post(models.Model): title = models.CharField(max_length=80) author = models.ForeignKey(User, on_delete= models.CASCADE) content = models.TextField() date_posted = models.DateTimeField(default=timezone.now) def __str__(self): return self.title class Meta: verbose_name = "Blog Post" verbose_name_plural = "Blog Posts" ordering = ["-date_posted"] def get_absolute_url(self): return reverse('post-detail', kwargs={'pk': self.pk})
16.3. Customize Admin Site with ModelAdmin Class#
In this section, we’ll take a look at how to use the ModelAdmin
class to customize the admin site.
16.3.1. Control List Display#
The
list_display
attribute allows you to control which model fields are displayed on the model list page.Another great thing about it is that it can display related model fields using the
__
operator.Here’s the demo script
# blog/admin.py @admin.register(Post) class PostAdmin(admin.ModelAdmin): # Fields to display in the list view list_display = ('title', 'author', 'date_posted') # Displays these fields in the list view # Default ordering of records ordering = ('-date_posted',) # Orders the posts by date_posted in descending order # Read-only fields in the form readonly_fields = ('date_posted',) # Makes date_posted field read-only # Custom admin for the Comment model @admin.register(Comment) class CommentAdmin(admin.ModelAdmin): # Fields to display in the list view list_display = ('post', 'author', 'date_posted') # Displays post, author, and date_posted in list view # Default ordering of records ordering = ('-date_posted',) # Orders comments by date_posted in descending order # Read-only fields in the form readonly_fields = ('date_posted',) # Makes date_posted field read-only
16.3.2. List Display Custom Fields#
The
list_display
setting can also be used to add custom fields.To add a custom field, you must define a new method within the
ModelAdmin
class.Here’s the demo script
# blog/admin.py @admin.register(Post) class PostAdmin(admin.ModelAdmin): # Fields to display in the list view list_display = ('title', 'author', 'date_posted','comment_count') # Displays these fields in the list view # ... # Method to count comments for each post def comment_count(self, obj): return obj.comments_count comment_count.short_description = 'Number of Comments' # Sets the column header name in the admin # Optimizing query with annotation def get_queryset(self, request): queryset = super().get_queryset(request) # Annotate each post with the count of related comments queryset = queryset.annotate(comments_count=Count('comment')) return queryset
Explanation
comment_count(self, obj)
Method:This method is used to display the number of comments related to each post.
It uses the
comments_count
value annotated to each post in theget_queryset
method.short_description
is used to define the label that will appear as the column header in the admin list view.
get_queryset(self, request)
:This method overrides the default
get_queryset
of theModelAdmin
to annotate each post with the count of its related comments.The
annotate(comments_count=Count('comment'))
line adds acomments_count
field to each post object, counting the related Comment instances.
16.3.4. Filter Model Objects#
Django admin makes it easy to filter objects.
Best of all, Django can stack filters – e.g., filter by two or more fields simultaneously.
To filter by a related object’s fields, use the
__
operator.For more advanced filtering functionality, you can also define
custom filters
.To define a custom filter, you must specify the options or so-called
lookups
and aqueryset
for eachlookup
Here’s the demo script to filter
Comment
byposted_date
ofPost
from django.utils import timezone from datetime import datetime, timedelta # Custom filter to display post's posted date with a custom name class PostPostedDateFilter(admin.SimpleListFilter): title = 'Post Posted Date' # Sets the display title for the filter parameter_name = 'post__date_posted' # The field name used in the query parameters def lookups(self, request, model_admin): """ Returns a list of tuples. Each tuple contains a value and a display name for the filter options. """ return [ ('today', 'Today'), ('past_7_days', 'Past 7 days'), ('this_month', 'This month'), ('this_year', 'This year'), ] def queryset(self, request, queryset): """ Filters the queryset based on the selected filter option. """ today = timezone.now().date() if self.value() == 'today': return queryset.filter(post__date_posted__date=today) elif self.value() == 'past_7_days': past_7_days = today - timedelta(days=7) return queryset.filter(post__date_posted__date__gte=past_7_days) elif self.value() == 'this_month': return queryset.filter( post__date_posted__year=today.year, post__date_posted__month=today.month ) elif self.value() == 'this_year': return queryset.filter(post__date_posted__year=today.year) return queryset # Default returns all if "Any date" is selected or no option matches # Custom admin for the Comment model @admin.register(Comment) class CommentAdmin(admin.ModelAdmin): # ... # Fields to filter the records list_filter = ('author', 'date_posted',PostPostedDateFilter) # Adds filters for post, author, and date posted # ...
16.3.5. Search Model Objects#
Django admin
provides basic search functionality.It can be enabled by specifying which model fields should be searchable via the
search_fields
attribute.Here’s the demo script
@admin.register(Post) class PostAdmin(admin.ModelAdmin): # ... # Fields that are searchable search_fields = ('title', 'content') # Allows searching through titles and content # ... # Custom admin for the Comment model @admin.register(Comment) class CommentAdmin(admin.ModelAdmin): # ... # Fields that are searchable search_fields = ('content',) # Allows searching through the comment content # ...
16.3.6. Handle Model Inlines#
The
admin interface
allows you to editmodels
on the same page as the parent model viainlines
.Django provides two types of
inlines
StackedInline and TabularInline.Let’s use an inline to display
Comments
on thePost details page
# Inline admin to display comments directly within the Post admin class CommentInline(admin.TabularInline): model = Comment extra = 0 # Number of empty forms displayed to add new comments readonly_fields = ('author', 'date_posted') # Display these fields as read-only @admin.register(Post) class PostAdmin(admin.ModelAdmin): # ... # Inlines allow comments to be managed directly within the Post form inlines = [CommentInline] # Displays related comments inline with the Post
Visit the admin site now, you should able to see the comments on the post detail page
16.3.7. Custom Admin Actions#
Django admin actions
allow you to perform an “action” on an object or a group of objects.An
action
can be used to modify an object’s attributes, delete the object, copy it, and so forth.Suppose we try to define
empty_comment
to cleancontent
of selected comment@admin.action(description="Clean selected comments' content") def empty_comments(modeladmin, request, queryset): queryset.update(content='') # Custom admin for the Comment model @admin.register(Comment) class CommentAdmin(admin.ModelAdmin): # ... actions = [empty_comments]
Now you can see the added actions in admin site
16.4. Override Django Admin Templates#
The Django admin site
allows you to customize any visual aspect of it by overriding templates
. All you have to do is:
Check out Django’s source code and copy the original template.
Paste the template in
templates/admin
ortemplates/registration
, respectively.Modify the template to your liking.
Most of the time, you’ll be able to get away with just changing a portion of the original template
. For example, if we want to add a message
above the login form
, we can inherit from login.html
and then change the content_title
block:
<!-- templates/admin/login.html -->
{% extends "admin/login.html" %}
{% block content_title %}
<p style="background: #ffffcc; padding: 10px 8px">
This is a really important message.
</p>
{% endblock %}
Navigate to your login
page, and you should be able to see the yellow message.
16.5. Style Admin Site with Django Admin Interface#
16.6. Extensions#
16.6.1. Meta class options#
Here I list more commonly used Meta
class options, check it out.
Meta Option |
Purpose |
Code Example |
---|---|---|
|
Sets a human-readable singular name for the model. |
|
|
Sets a human-readable plural name for the model. |
|
|
Specifies the default ordering of model instances. |
|
|
Enforces a unique constraint on the specified fields. |
|
|
Defines custom permissions for the model. |
|
|
Sets the name of the database table used for the model. |
|
|
Defines the default field used when retrieving the latest object. |
|
|
Creates database indexes on specified fields for faster lookups. |
|
|
Specifies that the model is abstract and will not be created as a database table. |
|
|
Defines custom database constraints on the model. |
|
|
Specifies the default permissions (add, change, delete, view) for the model. |
|
|
Creates a proxy model that inherits from another model, used for different model behavior. |
|
|
Indicates if Django should manage the model’s database table (e.g., create/drop tables). |
|
|
Indicates that the model was automatically created (typically for intermediate tables). |
|
|
Forces Django to select the instance from the database immediately after saving. |
|
|
Sets the default reverse relationship name for foreign key fields. |
|
|
Creates a composite index on the specified fields for efficient multi-column searches. |
|
|
Customizes the application labels associated with the model (used in advanced scenarios). |
|
|
Specifies the name of the default manager for the model. |
|
16.6.2. Inline options#
Here’s a table introducing commonly used methods and properties of
admin.StackedInline
andadmin.TabularInline
.These two classes are used in
Django’s admin interface
to display related models directly within theparent model's form
, enhancing the data management experience.
Method/Property |
Purpose |
Code Example |
---|---|---|
|
Specifies the model to be displayed as an inline within the parent model’s admin page. |
|
|
Defines the number of empty forms displayed by default when adding related objects. |
|
|
Specifies the fields to display in the inline form. |
|
|
Displays specified fields as read-only, preventing edits directly in the inline form. |
|
|
Sets the maximum number of forms that can be added in the inline. |
|
|
Sets the minimum number of forms that must be present in the inline. |
|
|
Specifies a custom foreign key to use if the related model has more than one foreign key to the parent model. |
|
|
Customizes the singular name of the inline object in the admin interface. |
|
|
Customizes the plural name of the inline object in the admin interface. |
|
|
Allows or disallows deletion of the inline objects directly in the parent form. |
|
|
Displays a link to edit each related object from the inline list view. |
|
|
Allows customization of the formset class used for the inline, enabling more complex validation or custom behavior. |
|
|
Specifies a custom template for rendering the inline form, allowing for deeper UI customization beyond default Tabular or Stacked layouts. |
|
|
Adds CSS classes to the inline section for styling purposes. |
|
|
Defines the default ordering of the related objects within the inline form. |
|
|
Excludes specific fields from being displayed in the inline form. |
|
|
Displays specified fields in a tabular format in the list view. Used specifically with |
|
|
Automatically populates fields based on the values of other fields, often used with slugs. |
|
|
Allows sorting of inline objects by a specific field, typically used when items are ordered manually (like drag-and-drop sorting). |
|