How to Hack, Redesign, & Customize the Django Admin with Bootstrap

Here’s a great article from SitePoint

The Django administration site is great — fully-featured, easy to use, secure by design, rock solid … and somewhat ugly, which can be something of a downside when you want to integrate it with the look and feel of the rest of your website. Let’s sort that out.

If It Ain’t Broke …

The default Django admin
The default Django admin. (Source)

Say you’ve just prototyped a web app with Django and Vue.js. For a wide array of cases, using Django’s admin for back office purposes as is, and even handling it over to your client after appropriately setting permissions, is just fine. After all, it works perfectly well and it can be heavily customized with the built-in tools to cover many situations.

So again, why bother?

Reasons to Hack the Look and Feel of the Admin

However, there are a number of valid reasons to take integration a step further:

  • Branding: there’s nothing wrong in wanting the name and colors of your company instead of “Django administration” (and for the record, this is in compliance with Django’s BSD license).
  • Seamless integration between main site and admin: you might want to be able to transition between back office functionality while navigating the site, and vice versa, by having a common navigation bar.
  • Prettifying: while the admin looks okay, and it has even implemented responsive web design principles ever since v2 (it works well on both, mobile and desktop), there’s a lot a well-crafted style sheet can do to make it look better.
  • Bypass functionality: you might also just want to create custom dropdown menus for the admin, displaying the options that you actually use and hiding from the user interface what you don’t really need, which could make for a better user experience.

A Practical Example

For this example, and not to repeat ourselves, we’ll resume the simple publishing web application we started for the Prototyping a Web App with Django and Vue.js article.

In a nutshell:

  • a Django app with two models:
  • Article with fields name author (linked), content and slug
  • Author: with fields name and slug
  • A single view called frontend that queries all registries in both models.
  • A single template called template.
  • Implementation of Vue.js with Vue Router and Vuex for a reactive scalable interface.

We won’t particularly care for the Vue.js integration in this installment, and we won’t modify it here.

The Basic Template

Source

Django templates are very versatile and powerful, and can either be created at the app level (a component of the Django site) or at the site level, and can even override the templates that come with Django (which is what we’ll do here).

Bootstrap logo
Source

We created a basic template that links to Bootstrap‘s JavaScript and style sheet, and also its companion tools, jQuery and Popper.

Here’s the base template we’re using for the main site, not at all different from what we would normally use for any other Django site:

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">

    <title>Django and Vue.js</title>
  </head>
  <body class="bg-light">
    <div class="bg-white container">
      <h1>Prototyping a Web App with Django and Vue.js</h1>

      <!-- Content -->
    </div>

    <!-- Vue.js -->
    <script src="https://unpkg.com/vue"></script>
    <script src="https://unpkg.com/vue-router"></script>

    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
  </body>
</html>

Next, we’ll integrate this into the admin, and add a shared navigation bar across both ends — the main site and the back office!

Integrating the Main UI Template with the Admin

As mentioned, we can override templates, including those of the admin. However, because of Django’s design, and unsurprisingly, the main site and the back office are two different systems, each with its own templates, style sheets, and contrib packages. So even if they will be almost identical, we’ll need to maintain two different templates — one for the main UI, and one for the admin.

Enabling a Directory for Templates in General

First, we need to tell Django where we’ll store the hacked admin template in the base directory.

Se we’ll need to edit myproject/settings.py. firstly, find the TEMPLATES constant and this DIRS key:

'DIRS': [],

Change that code to this:

'DIRS': [os.path.join(BASE_DIR, 'templates')],

Wrapping the Admin Template (admin/base Hack)

If we just wanted to do cosmetic changes, like passing a custom style sheet to the admin, or removing/replacing its header, we could get along with that by just editing the admin/base_site template and skipping this current step altogether. However, if we want to go all the way and “wrap” the admin section as if it was contained within our main site, with the possibility to have a common header and footer, then keep reading.

We’ll need to copy Django’s admin/base.html to our templates directory in templates/admin/base.html, so that we can place our wrappers.

We’ll edit the code around the container section, so that it goes from this:

<!-- Container -->
<div id="container">
(...)
</div>
<!-- END Container -->

to this:

{% block bodyheader %}{% endblock %}

<!-- Container -->
<div id="container">
(...)
</div>
<!-- END Container -->

{% block bodyfooter %}{% endblock %}

And that’s all! We simply created bodyheader and bodyfooter block tags, so that we could inject the code that will wrap the admin on the next step.

Coding a Custom Admin Template (admin/base_site Hack)

Then, we’ll code the actual template in templates/admin/base_site.html (we’ll need to create the directories on the root of our project):

{% extends "admin/base_site.html" %}

{% block title %}Django with Bootstrap | Admin site{% endblock %}

{% block branding %}{% endblock %}
{% block breadcrumbs %}{% endblock %}

{% block bodyclass %}bg-light{% endblock %}

{% block extrastyle %}
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <style>
      #header, .breadcrumbs { display: none; }

      /* Bootstrap issues with admin */
      * { box-sizing: unset; }
      div.module caption { caption-side: top !important; }
      .collapse { display: block !important; }
    </style>
{% endblock %}

{% block bodyheader %}
    <div class="bg-white container">

      <div class="jumbotron">
        <h1 class="display-4">Hacking the Django Admin with Bootstrap</h1>
        <p class="lead">
          The <a ref="https://docs.djangoproject.com/en/dev/ref/contrib/admin/">Django administration site</a> is great—full-featured, easy to use, secure by design, rock solid… and somewhat ugly, which can be something of a downside when you want to integrate it with the look-and-feel of the rest of the website. Let’s sort that out.
        </p>
      </div>
{% endblock %}

{% block bodyfooter %}
    </div>

    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
{% endblock %}

Breakdown

Let’s try to explain what we’re doing here:

  1. We tell the template engine that we are “extending” the admin/base_site.html template, to effectively override some of its definitions.
  2. We make use of the title block to customize a title for the admin page being browsed.
  3. We empty the content of branding and breadcrumbs blocks, as we don’t really need them.
  4. We use the bodyclass block to set Bootstrap’s bg-light, as we did in the frontend template.
  5. We use the extrastyle block to embed Bootstrap, and some CSS code.
    a. Okay, #header, .breadcrumbs { display: none; } is something of a restatement of number 3; but it’s useful to know you can disable the branding and breadcrumbs sections both ways.
    b. There can be some issues when overlapping Bootstrap with Django’s CSS in the admin, so these are some fixes.
  6. Use the bodyheader and bodyfooter blocks to wrap the admin content.

Now that we have access to the admin template, we could further its style sheet, or just leave it at that with a shared style with the main UI.

Caveats

We’re maintaining two different templates (main UI and admin) to do essentially the same presentation. Admittedly, this isn’t ideal, as we’re explicitly breaking one of the maxims of software development: don’t repeat yourself (DRY).

As we commented, this is because the Django admin has been designed to be detached from the main UI. And there’s nothing wrong with that, just as there isn’t anything wrong with thinking out of the box. But yes, that forces us to use two templates with nearly the same content.

Actually, in principle we could design a template pattern that included that navbar and other common elements from the main UI and the admin, and reuse them from that single source; but at this point, and for the purpose of this article, that approach would be a little overkill. Anyway, I’ll leave the idea planted for you. 😉

Continue reading
How to Hack, Redesign, & Customize the Django Admin with Bootstrap
on SitePoint.

Source link