Optimization Made Easy in the Django Admin

Intro

Django is a Python-based web application framework that makes it easy to start developing a new dynamic website and simplifies much of the solved boilerplate a developer would be responsible for implementing without a framework, such as user authentication, session management, security, and so on. While frameworks do introduce overhead, that is best countered with tactics such as caching as a site grows, most sites never approach the level of traffic where these sorts of concerns require especially clever tactics to counter. Django is forward-thinking in this regard, providing ample documentation of how straightforward usage can be tweaked to give way to powerful underlying abstractions for performance optimization. Django allows developers to deploy robust websites quickly, providing new experiences to users as fast as possible. When framework naysayers claim that development velocity is not worth framework bloat, remember Donald Knuth’s classic quote.

The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming.

Django 3.0 was released less than one week ago, marking two years since the release of Django 2.0, which removed support for Python 2. While Django 3.0 does not take as drastic a leap of removing support for a major version of Python, it moves the Django landscape forward in exciting ways, such as setting the groundwork for full asynchronous support within Django internals!

Many here at JBS are big fans of Django and it is an integral part of many of our projects. In fact, JBS is a sponsor of Djangocon, the annual Django convention that explores Django usage for all experience levels and helps developers learn how to contribute value to open source software such as Django.

Django provides an excellent tutorial that can have you working inside your first Django app at no time at all. Toward the end of your tutorial you should come upon the admin, and here we'll explore one aspect of it.

Admin

One of Django’s most useful built-in features for users is the django admin site. The Django admin provides a complete CRUD UI for content administrators to manage objects represented in ORM models. The admin can be extended using other Django built-ins such as permissions to facilitate exposing certain operations to only specific users, and is designed with straightforward overrides that allow for custom theming, adding new views, customizing forms, and so on.

An under-advertised and exceptional feature added to the admin two years ago in 2.0 is built-in autocomplete for related fields. Autocomplete exposes form choices using Select2, an enhanced Select-like form element that exposes searching via user-provided queries using AJAX to backend views. Select elements with a lot of options and queries that return large amounts of data are both easy targets for slow page loading performance. Reducing queries when the user first visits a page greatly reduce the time to load all front-end asset, and limiting elements rendered on a form keep it snappy as it grows larger. A simple example can show how autocomplete can quickly result in real performance gains in the admin.

dj-liftoff.png

In a django project we create a model, related model, and register the model so that it will appear in the admin, then populate it with some common data (timezone names) in the shell:

models.py

from django.db import models

class Timezone(models.Model):
    name = models.TextField()

    def __str__(self):
        return self.name

class TestModel(models.Model):
    tz_1 = models.ForeignKey(Timezone, on_delete=models.CASCADE, related_name='tz_1')
    tz_2 = models.ForeignKey(Timezone, on_delete=models.CASCADE, related_name='tz_2')

admin.py

from django.contrib import admin
from . import models

@admin.register(models.TestModel)
class TestModelAdmin(admin.ModelAdmin):
    pass

REPL

In [1]: import pytz

In [2]: for tz in pytz.all_timezones:
   ...:     models.Timezone.objects.create(name=tz)

dj-admin-two-select.png

Google Chrome shows our admin view taking 326ms to load. If we try to search “bum”, we land on “Brazil/Acre”.

dj-admin-two-select-selected.png

What can we do to make this better? A couple small changes to admin.py

from django.contrib import admin
from . import models

@admin.register(models.Timezone)
class TimezoneAdmin(admin.ModelAdmin):
    search_fields = ['name']

@admin.register(models.TestModel)
class TestModelAdmin(admin.ModelAdmin):
    autocomplete_fields = ['tz_1', 'tz_2']

Google Chrome shows our admin view taking 59ms to load. An over 5x speedup! If we try to search “bum”, we land on an entry containing that term!

dj-admin-autocomplete-selected.png

Django’s admin machinery takes care of setting up a view for our form to call and swapping the Select2 in for our traditional Select element.

Conclusion

The Django web framework is a powerful tool that helps web developers get exciting website deployed quickly and provides a litany of options for all sorts of use cases. We’ve only just scratched the surface of the admin and briefly explored a feature somewhat buried in the documentation that can greatly enhance UX and speed up loading performance with just a couple lines of code! The Django documentation is full of gems, offering exciting ways to leverage this mature framework.

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