Customizing the Django Admin

43 minute read     Updated:

Chepkirui Dorothy %
Chepkirui Dorothy

This article explores the customization of the Django admin interface. Earthly significantly accelerates build workflow with its parallel build capabilities. Check it out.

The Django framework comes with an admin site which is a quick, model-centric interface that Django creates by reading metadata from your models. The interface allows trusted users to manage model content on your site. You can easily create, read, update, and delete model content on the admin site. This saves you a lot of time during development.

This tutorial will focus on customizing the Django Admin site. You will create a simple SocialApp to work with. The SocialApp app will have two models, the Author model, and the Post model. The Author model will have a first and last name field, as well as an image field. The Post model will include the author, the content posted, and the date it was created.

We will assume a working understanding of how Django works.

Note that this article uses version 4.1.2 of Django.

In this tutorial, you will learn to customize the admin site with the following features: controlling field display, disabling models, making lists searchable, adding filters, thumbnails, links, custom validations, and overriding templates and forms.

All the code examples in this tutorial can be found in this Github repository

Project Setup

Set up a Django project and create a Django app to follow up with the rest of this tutorial. I will set up my project and app and call them SocialApp_project and SocialApp respectively.

Register the SocialApp app you created in the list of INSTALLED_APPS in the file. Run migrations for the apps that are installed in Django by default.

Creating the Model

Add the following in the

from django.db import models

class Author(models.Model):

    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    image = models.ImageField(upload_to="images", blank=True, null=True)

    def __str__ (self):
        return self.first_name

class Post(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    date=models.DateTimeField(auto_now_add = True)

    def __str__ (self):
        return self.title

These models represent an author and a post. The Author model has a first_name, last_name, and an image field. The image will be uploaded to the images folder you specified in the image field. The ImageField requires that you install the Pillow package.

You can install Pillow with the pip package manager as shown below:

pip install pillow

The Post model has a title field for the post title, a details field for the post details, and a date field for the date the post was created.

Configuring the Media File

To configure the media files, you will need to specify the location where all the uploaded media files will be stored. You can do this by defining the MEDIA_ROOT and MEDIA_URL in the file:

Add the following code snippet to

import os

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

The MEDIA_ROOT setting specifies the path to where the media files are stored. The path is relative to the root directory (BASEDIR), while the MEDIA_URL setting defines the base URL for accessing these media files. This configuration will let Django create an images directory under the media folder when the author uploads an image.

Registering the Models

For a model to be visible on the Django admin site, you need to register it in the file. You can register the model alone or register the model with a subclass of the ModelAdmin if you want to customize the default interface that Django creates for the model.

The ModelAdmin is a class that contains all the information required to define the interface used to handle a specific model. When you define a ModelAdmin class for a model, you can customize the appearance and behavior of the admin interface for that model, such as making the list of model objects searchable.

When you register a model alone, Django will use the default ModelAdmin class, which provides a basic interface for working with the records for the model. In this case, you don’t have to define a custom ModelAdmin class, and you don’t have any control over the appearance or behavior of the admin interface for the model.

The following code registers the models with the default ModelAdmin class:

from django.contrib import admin
from .models import  Author, Post

If you want to customize the appearance or behavior of the admin interface for a model, you can create a subclass of the ModelAdmin class and register the model with that class.

You can register the model with a subclass of the ModelAdmin class as shown below:

from django.contrib import admin
from .models import  Author, Post

class   AuthorAdmin(admin.ModelAdmin):
    pass, AuthorAdmin)

class PostAdmin(admin.ModelAdmin):
    pass, PostAdmin)

Alternatively, you can register the models by decorating the subclass of the ModelAdmin class with the admin.register() decorator:

from django.contrib import admin
from .models import  Author, Post

class   AuthorAdmin(admin.ModelAdmin):
    pass, AuthorAdmin)

class PostAdmin(admin.ModelAdmin):
    pass, PostAdmin)

Accessing the Admin Site

Only a superuser or an admin user can access the admin site. Therefore, you need to create one.

In the terminal, create a superuser that can access the admin site:

./ createsuperuser

Follow the prompts, and you should create the superuser successfully.


To access the admin site, run the server.

./ runserver


Navigate to in your browser to access the admin site:

Django admin_site

You can add the author and post model objects here.

Click the Add button to add details of a new author. Input their first name, last name, and image, then click on the SAVE button:

Adding new Author

You can add the posts data the same way:

Adding new Post

Customizing the Admin Site with the ModelAdmin Class

The ModelAdmin class has a lot of options for you to modify the interface of the admin site. These options are usually specified as a class attribute or as a method in the ModelAdminclass.

Controlling the Fields to Display Using the list_display Attribute

The admin site only shows the string representation of a model when listing the model objects, for the Author model. It shows the first name which is the string representation of the Author model that you defined via the __str __ method.

    def __str__ (self):
        return self.first_name


Suppose you want to list the author’s first name, last name, and image in the interface, you can use the list_display option of the ModelAdmin class as shown below:

class   PostAdmin(admin.ModelAdmin):

    list_display = ( "title","author","details","date")
Displaying author_list

Clicking on the author’s first name will take you to the change author page, where you can alter the author’s details:

Django change_author page

Removing Models from the Admin Site

You can remove a model from the admin site by unregistering the model in the file. For example, you can remove the Group model of the auth app that Django includes by default. The auth app includes the User model and the Group model which are automatically included in the admin site.

To remove the default Group model or any model from the admin site, you need to unregister it in the file of your SocialApp project:

from django.contrib.auth.models import Group


Making the Model Object List Searchable

Django lets you create a search box in the admin interface by adding the search_fields attribute in the ModelAdmin class. This should be set to a list of field names that will be searched whenever somebody submits a search query in that text box.

You can add the seach_fields as shown below:

class AuthorAdmin(admin.ModelAdmin):

    list_display = ("first_name", "last_name","image")
    search_fields = ("first_name",)

Searching for a word will return all model objects where the fields you specified in search_fields contain the searched query.

The search bar comes in handy when dealing with a lot of data.

You can search the author’s list as shown in the image below:

Searching first_name

This searches through the list using the first_name field only. If you search using the last name, the results would be 0 authors.

Searching author

To search using the first_name and the last_name fields, add them to the search fields list:

class AuthorAdmin(admin.ModelAdmin):
    list_display = ("first_name", "last_name","image")
    search_fields = ("first_name","last_name")
Search using First & Last name

Adding Filters to the List of Model Object

You can specify a list_filter attribute in the ModelAdmin subclass to narrow down the search space based on the filter that you selected:

class AuthorAdmin(admin.ModelAdmin):
    list_display = ("first_name", "last_name","image")
    search_fields = ("first_name",)
    list_filter = ("first_name", )

The List filters appear in the admin’s right sidebar of the change list page:

Navigating search filter

The filter list is automatically populated with the first name values of the authors.

Search filter_last_name

By clicking on a name, you will change the filter list to display only users with the selected first name.

Adding Image Thumbnails

So far, images have been listed with their respective URLs, you can choose to display the image thumbnails instead.

To display the thumbnail for a model object, you will need to define an image_tag method in the model class of the object. The method returns an HTML img tag. You will need the Django’s mark_safe function to mark the HTML tag safe for output. The src attribute of the img tag will specify the path to the image.

Add the following in the Author model in

from django.utils.html import mark_safe

class Author(models.Model):

    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    image = models.ImageField(upload_to="images", blank=True, null=True)

    def __str__ (self):
        return self.first_name

    def image_tag(self):
        return mark_safe('<img src="%s" width="100px" height="100px" />'%(self.image.url))
    image_tag.short_description = 'Image'

The mark_safe function marks a string explicitly as safe for (HTML) output. The returned object can be used in any situation where a string or unicode object would be appropriate.

Add the image_tag to the list_display attribute in

class AuthorAdmin(admin.ModelAdmin):

    list_display = ("first_name", "last_name","image_tag")

You will have to configure media urls in the before the images can appear.

Add the following in the

from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

This sets up a URL mapping to serve the images in the MEDIA_ROOT specified in the project’s settings, allowing you to serve user-uploaded files.

This only works in debug mode

The thumbnail of the images is displayed on the admin site as shown below:

Author’s image_tag

Linking Other Object Pages

Objects can refer to other objects using foreign keys. The post model has a many-to-one relationship with the author via ForeignKey which is displayed as an unclickable text on the admin site. The title fields of the Post model are clickable, making it easy to change their details. If you want to change the author’s details, you must go to the Author admin page to make the relevant changes. This can become tedious if a lot of changes are to be made.


However, you can use Django’s URL-reversing system to access the related object’s admin page and make the changes more efficiently. URL reversing refers to the process of converting a named URL pattern to a URL string that can be used in an HTTP request. This makes it easier to link between pages, as you can refer to named URL patterns instead of hardcoded URLs.

from django.utils.html import mark_safe
from django.urls import reverse

class   PostAdmin(admin.ModelAdmin):

    list_display = ( "title","author_link","details","date")
    def author_link(self, obj):
        url = reverse("admin:SocialApp_author_change", args=[])
        link = '<a href="%s">%s</a>' % (url,
        return mark_safe(link)
    author_link.short_description = 'Author'

This code defines a custom method author_link which takes a Post object (obj) and returns a link to the change page of its related author.

The URL for the author change page is generated using Django’s URL reversing system and the reverse function with the named URL pattern "admin:SocialApp_author_change" that Django creates for the author change page.

The first part of the name, admin, is a namespace that refers to the Django administration interface. SocialApp_author_change is a string that represents the URL pattern name for the change page of the author of a SocialApp model.

The naming follows a specific format: admin:<app_label>_<model_name>_<action>, where app_label refers to the name of the Django app where you defined the model. The model_name is the name of the model, in lowercase and action is one of several possible actions, such as change, delete, or add.

The author’s ID is passed to the URL pattern with, which returns the ID of the related Author instance. This ID is used to generate a URL to the change page of the Author model, which is then used in the link variable to create a link to the change page of the Author in the Django admin view. The mark_safe function is used to indicate that the returned string is safe to be displayed. The short_description attribute is set to "Author" for the header in the Django admin site.

This will hyperlink the author field to its change view, where you can change their first name, last name, and image by just clicking on the hyperlinked first name from the post view:

Navigating to change Author details

The change view:

Changing Author details

Adding Custom Validation to the Admin

In a standard web application, users enter data through forms that are then stored in a database. It’s crucial to verify that the stored data is valid and meets specific criteria. For instance, email addresses must adhere to a particular format, passwords should be of a minimum length, and dates must be set to occur in the future. We can validate this data with a validator.

A validator is a callable that accepts a value and raises a ValidationError if the value doesn’t satisfy specific requirements. When you try to save objects with invalid values, the Django admin site will indicate a ValidationError. Depending on your project’s needs, you will frequently need to create custom validators and raise custom exceptions. You will need to override the clean method of the models.Model class to perform additional validation on a model before saving it to the database.

Suppose you want the title to have a minimum character limit of 10 letters, add a clean method in the Post model as shown below:

from django.core.exceptions import ValidationError

class Post(models.Model):
   def clean(self):
       if  len(self.title) > 10:
           raise ValidationError(
               {'title': "Title should have at least 10 letters"})

The clean method is used in Django forms to perform custom validation logic on the form data. This helps ensure that the data entered by the author is consistent and valid before it is saved to the database, avoiding data integrity and consistency issues. If the validation is successful, no errors are raised and the method simply returns None. If you enter a title that has a length that is less than 10, the validation fails and a ValidationError exception is raised:

Title limit of new post

Overriding the Admin Templates

You can override many of the templates the admin module uses to generate the various pages of an admin site. This allows you to customize the appearance of the admin site to match your site’s design, or to provide a better user experience.

The admin templates folder is stored in your virtual environment.

├── admin
│ ├── 404.html
│ ├── 500.html
│ ├── actions.html
│ ├── app_index.html
│ ├── app_list.html
│ ├── auth
│ │ └── user
│ │ ├── add_form.html
│ │ └── change_password.html
│ ├── base.html
│ ├── base_site.html
│ ├── change_form.html
│ ├── change_form_object_tools.html
│ ├── change_list.html
│ ├── change_list_object_tools.html
│ ├── change_list_results.html
│ ├── date_hierarchy.html
│ ├── delete_confirmation.html
│ ├── delete_selected_confirmation.html
│ ├── edit_inline
│ │ ├── stacked.html
│ │ └── tabular.html
│ ├── filter.html
│ ├── includes
│ │ ├── fieldset.html
│ │ └── object_delete_summary.html
│ ├── index.html
│ ├── invalid_setup.html
│ ├── login.html
│ ├── nav_sidebar.html
│ ├── object_history.html
│ ├── pagination.html
│ ├── popup_response.html
│ ├── prepopulated_fields_js.html
│ ├── search_form.html
│ ├── submit_line.html
│ └── widgets
│ ├── clearable_file_input.html
│ ├── foreign_key_raw_id.html
│ ├── many_to_many_raw_id.html
│ ├── radio.html
│ ├── related_widget_wrapper.html
│ ├── split_datetime.html
│ └── url.html
└── registration
├── logged_out.html
├── password_change_done.html
├── password_change_form.html
├── password_reset_complete.html
├── password_reset_confirm.html
├── password_reset_done.html
├── password_reset_email.html
└── password_reset_form.html

The admin templates are saved in two directories: Registration - Templates for the various Django admin page, password change actions, and the Django admin logout page layout. Admin - for the model object pages

You can create a new template directory and copy the templates you want to override and paste them into your new templates directory. Django will load these templates instead.

Create a new folder called templates in the root directory and configure the to include the new template path.

        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR/'templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [

This defines a list of dictionaries, where each dictionary represents a template backend configuration. In this case, the only template backend defined is django.template.backends.django.DjangoTemplates. The DIRS option specifies the directories where Django should look for templates. In this case, the directory is set to BASE_DIR/templates, which means that templates should be stored in a directory called templates located within the BASE_DIR (The root directory). The APP_DIRS option is set to True which means that Django will also look for templates in the templates directory of the installed apps.

The OPTIONS dictionary contains additional settings for the template engine like the context_processors setting. The context_processors option allows you to specify a list of functions to be run before a template is rendered. These functions are called context processors and their purpose is to add additional data to the context that is passed to the template. Copy the templates you want to change into the new templates directory. The index.html file under the admin folder controls how the homepage of the admin behaves. To alter it, create a new folder under the templates folder called the admin folder, and add the index.html file to the folder.

The file tree is as shown below:

└── admin
└── index.html

You can change the colors of the Recent actions and My actions titles as shown below:

{% block sidebar %}
<div id="content-related">
<div class="module" id="recent-actions-module">
<h2 style="color:purple">{% translate 'Recent actions' %} </h2>
<h3 style="color:red">{% translate 'My actions' %}</h3>


{% endblock %}
Customized colors to template

Overriding the templates will give you a personalized look at each admin page.

Overriding Django Admin Forms

Django provides the ModelForm class, which provides a convenient way to create and update records in the database. By using a model form, you can automatically generate a form based on the fields defined in a Django model. This saves you from having to write HTML for each form field and also ensures that the form fields match the database fields exactly.

However, you can alter the look of the forms to suit your needs. For example, if the post details contained more words and needed more space to edit them, you would need to increase the editing area.

Post’s textarea

from django.forms import ModelForm, Textarea

class PostForm(ModelForm):
    class Meta:
        model = Post

        fields = '__all__'

        widgets = {
            'details': Textarea(attrs={'cols': 130, 'rows': 20}),

@admin.register(Post)class   PostAdmin(admin.ModelAdmin):

    form = PostForm

This is a Django form defined using the ModelForm class. It represents a form for creating or updating instances of the Post model.

The form will contain all fields of the Post model, as specified by the fields attribute in the Meta class. Additionally, the widgets attribute in the Meta class is used to specify the widget to be used for the details field, which is a textarea with 130 columns and 20 rows.

This will increase the editing area as shown below:



Customizing the Django admin site can drastically improve user experience and data management efficiency in a Django project. With simple adjustments, it can evolve into a powerful, tailor-made tool. This tutorial taught you how to customize field display, disable models, add filters, links, thumbnails, custom validations, and override templates and forms.

For a deeper dive into admin interface customization, check out the Django documentation. And once you’ve mastered Django admin customization, why not take your project to the next level? Optimize your builds with Earthly, a tool designed to streamline and enhance your build process.

Earthly Cloud: Consistent, Fast Builds, Any CI
Consistent, repeatable builds across all environments. Advanced caching for faster builds. Easy integration with any CI. 6,000 build minutes per month included.

Get Started Free

Chepkirui Dorothy %
Chepkirui Dorothy
She is a backend developer and a technical writer. She has a passion for writing and is constantly looking for new tools and techniques to hone her skills.
Writers at Earthly work closely with our talented editors to help them create high quality content. This article was edited by:
Mustapha Ahmad Ayodeji %
Mustapha Ahmad Ayodeji

Ahmad is a Software developer and a Technical writer with so much interest for Django related frameworks.



Get notified about new articles!
We won't send you spam. Unsubscribe at any time.