Improve Django’s Admin Interface Using Inlines

Django Admin Customization

Django is an open-source python based web framework. It is designed to allow for rapid development of web applications by including much of the expected functionality right out of the box.

Django projects come prepackaged with a lot of convenient features. One prominent example of this is the built-in admin site. It offers users a way to add, remove, or edit the content of your apps by accessing the database with an easy to understand interface. The admin interface is also customizable in many ways. This post is going to focus on one such customization, something called inlines.

When two Django models share a foreign key relation, inlines can be used to expose the related model on the parent model page. This can be extremely useful for many applications. I have some code examples below to demonstrate, but there are numerous examples of real-world use cases in eCommerce, publishing, and more. Most recently, I used inlines in a professional setting to allow admin users to associate a single sign-on to a school organization in a large School Management System..

Inlines come in two main flavors - tabular and stacked. I will demonstrate each of them below. They mainly differ in terms of template layout and appearance.

Here is the models.py file for the Starfleet app. As you can see, there is a foreign key relation between the Officer class and the Ship class via the ship_assignment field.

from django.db import models

# Create your models here.

class Officer(models.Model):
    name = models.CharField(max_length=256)
    rank = models.ForeignKey('Rank', on_delete=models.CASCADE)
    ship_assignment = models.ForeignKey('Ship', on_delete=models.CASCADE, blank=True, null=True)

    def __str__(self):
        return self.name


class Rank(models.Model):
    name = models.CharField(max_length=256)

    def __str__(self):
        return self.name


class Ship(models.Model):
    name = models.CharField(max_length=256)
    registry = models.CharField(max_length=256)
    captain = models.OneToOneField('Officer', default=None, on_delete=models.CASCADE, blank=True, null=True)
    ship_class = models.CharField(max_length=256)
    status = models.CharField(max_length=256)

    def __str__(self):
        return self.name


And here is the admin.py file for the Starfleet app. This example is fairly straight forward - the resulting admin interface is displayed below.

from django.contrib import admin
from .models import Officer, Rank, Ship

class OfficerAdmin(admin.ModelAdmin):
    model = Officer
    list_display = ['name', 'rank']

class ShipAdmin(admin.ModelAdmin):
    model = Ship
    list_display = ['name', 'registry']

class RankAdmin(admin.ModelAdmin):
    model = Rank
    list_display = ['name']


# Register your models here.
admin.site.register(Officer, OfficerAdmin)
admin.site.register(Rank, RankAdmin)
admin.site.register(Ship, ShipAdmin)

App Admin

I won’t belabor the point by showing every admin screen, but instead I will skip to the Change or Detail screens for the two related classes.

Change Officer

change_ship.png

Now by adding a Tabular Inline to the Ship class, it exposes the Officer model on the Ship model page, making it possible to add, remove or edit Officers directly from the model for the Ship to which they are assigned. Note that the OfficerTabularInline class references the Officer class in its declaration. It is also worth pointing out that in the ShipAdmin class, the inlines keyword argument requires either a tuple or a list.

from django.contrib import admin
from .models import Officer, Rank, Ship


class OfficerTabularInline(admin.TabularInline):
    model = Officer


class OfficerAdmin(admin.ModelAdmin):
    model = Officer
    list_display = ['name', 'rank']


class ShipAdmin(admin.ModelAdmin):
    inlines = [OfficerTabularInline]
    model = Ship
    list_display = ['name', 'registry']


class RankAdmin(admin.ModelAdmin):
    model = Rank
    list_display = ['name']

# Register your models here.
admin.site.register(Officer, OfficerAdmin)
admin.site.register(Rank, RankAdmin)
admin.site.register(Ship, ShipAdmin)

tabular_inline.png

To change it to a Stacked Inline we just have to replace the references to Tabular Inlines in the admin.py file:

class OfficerStackedInline(admin.StackedInline):
    model = Officer

... 

class ShipAdmin(admin.ModelAdmin):
    inlines = [OfficerStackedInline]
    model = Ship
    list_display = ['name', 'registry']

It is also possible to manage which fields are shown in the inline using the list_display and exclude keyword arguments in the inline class declaration.

stacked_inline.png

Introducing the JBS Quick Launch Lab!

FREE 1/2 Day Assessment

Quantify what it will take to implement your next big idea! Our intensive 1/2 day session will deliver tangible timelines, costs, high-level requirements, and recommend architectures that will work best, and all for FREE. Let JBS show you why over 20 years of experience matters.
Yes, I'd Like A FREE Assessment