Customizing the Django Admin

Easy Changes to Make the Admin User Friendly

About Lincoln Loop

  • Established in 2007
  • Open Source Contributor
  • Around 10 people
  • Multinational Team
  • Full-service Shop (design, dev, deployment, etc.)


  • User Interface is important
  • Default Admin UI is one-size-fits-all
  • User experience is not
  • What can we do to improve upon it?
  • How do we implement those improvements?

Software is Hard

Show of hands of who has pushed the admin onto a client. We do it too, but with disclaimers, and customizations.

Some of the problems we, as developers, face:

  • Clients want control over their web site
  • Minor changes, aka scope creep
  • Clients don't know Django
  • Little to no specification
  • Limited project and developer time
  • Etc, etc.

... So we write software to solve software problems. Amirite?

Problems with New Systems

  • Time consuming: Research, Specs, Dev, Test, Deploy
  • Even with testing, you might not nail it
  • Some reinventing of the wheel
  • Hard to estimate
  • Even more documentation to write
  • Stuff changes, custom systems need to keep up. More work. Blegh!

What's the Solution?

Leverage the Django Admin!

The Good News

  • Handful of reusable apps that alleviate some of the pain
  • Admin is (fairly) easy to extend, customize, etc.
  • Djangonauts are thinking about these problems. We care.
  • Lots of core functionality already exists in the admin. It's a time saver!

Why Care?

"Before we get to the bad news, let me answer the question on your face."

Whoa.. Why should I care what the client thinks about the admin experience?

- You


  • UI is the gateway to application logic
  • Users remember bad experiences, associate them with YOU and the admin
  • Good experiences = Happy Customers = Profit!
  • Contributing helps Django mature
  • Good UI reduces the need for documentation & support, but isn't a complete substitute!
  • It'll save you time and (some) headaches

Bad News

Not Informative or Assitive

  • No dashboard, statistics, or recent (user) activity
  • No actions (or models) highlighted or given priority
  • No assistance/help for beginner users
  • Impact from changes is not always clear
  • Disconnect from external systems

The admin doesn't help you help yourself! Blegh!

Dashboard & Primary Actions Example

Doesn't Fit Mental Models

The admin is generic and hasn't been tailored to how users think. Example, when adding an article, you may need categories, you may need meta information. Users don't think about this stuff - they think about adding articles.

  • Relationships (e.x. hierarchy) not always intuitive
  • Apps are not organized by context
  • Little (or no) navigation outside of breadcrumbs
  • Doesn't mimic familiar process and workflow (think about model requirements)

Grouping & Navigation Done Right

Admin Clutter

Missing Features

Users are spoiled by fancy pants features in other software.

  • Content and asset management tools, e.g. WYSIWYG, image manipulation
  • Error recovery
  • Export/Import with certain types
  • Inline help systems
  • Cross model search

If you hand the admin to these users, they will make the comparison.

Error Recovery

GMail makes it really easy to undo mistakes. Undo discard, undo move, etc.

Everyone makes mistakes. The admin is an unforgiving mistress.

Poor Display for Complex Models

Field Counts

Just how many fields were on that Satchmo model?

Your form is a conversation with your user.

Brainstorming Changes

  • Learn (or ask) about the industry, workflow, etc.
  • Understand the needs of your stakeholders (e.g. content editors vs. content approvers)
  • Put yourself in their shoes (empathy)
  • Learn the lingo, use it in your designs (laptop vs. notebook, memo vs. description)
  • Learn about previously used tools, pros and cons. Consider the PROS.

Django-Cms Concept



Customization Options

  • ModelAdmin media
  • Custom templates
  • ModelAdmin/ModelForm hacking
  • Custom views


Low hanging fruit for the win!

Low Hanging Fruit

ModelAdmin Media Code

1 class ArticleAdmin(admin.ModelAdmin):
2     class Media:
3         css = {
4             "all": ("my_styles.css",)
5         }
6         js = ("my_code.js",)

ModelAdmin Media Examples


  • WYSIWYG Editor
  • AJAX
  • Fancy Inlines (drag & drop)
  • Inject HTML


  • Colors
  • Layout

ModelAdmin Media Pros & Cons


  • Easy for one-off projects


  • Requires Javascript
  • Only works for the Change Form
  • Difficult to bundle as reusable app

Custom Templates

No Python required

Custom Templates

  • django.contrib.admin is a "reusable application"
  • Key templates:
    • admin/base.html
    • admin/index.html
    • admin/change_form.html
    • admin/change_list.html

Per Project/App/Model Templates

Templates can be overridden:

  • Across an entire project admin/change_form.html
  • Across an application admin/<my_app>/change_form.html
  • For an individual model admin/<my_app>/<my_model>/change_form.html

Custom Template Example


1 {% extends "admin/change_list.html" %}
3 {% block object-tools %}
4     <h1 class="errornote">
5         Look Here!
6     </h1>
7     {{ block.super }}
8 {% endblock %}

Custom Template Tips

  • Extend, don't override
  • Use {{ block.super }} to extend blocks
  • Extend a symlink of the admin templates in the event of recursion
  • Extend the extrahead block in base.html for admin-wide media

Custom Templates in the Wild





Custom Template Pros & Cons


  • Easy
  • Touches every admin view
  • No additional work to bundle with reusable apps


  • Mostly cosmetic (not functional) changes

ModelAdmin & ModelForm Hacking

Now we're getting serious

ModelAdmin/ModelForm Hacking

  • Added in Django 1.0 (newforms-admin)
  • Pre-1.0 stuff (list_display, fields, ordering, etc.)
  • 1.0 stuff (exclude, inlines, form, etc.)
  • 1.1 stuff (list_editable, actions, etc.)

Registering a ModelAdmin

 1 from django.contrib import admin
 2 from django.contrib.auth.admin import UserAdmin
 3 from demo_app.models import UserProfile
 5 class UserProfileInline(admin.TabularInline):
 6     model = UserProfile
 7     fk_name = 'user'
 8     max_num = 1
10 class CustomUserAdmin(UserAdmin):
11     inlines = [UserProfileInline]
14, CustomUserAdmin)

Row-Level Permissions

1 class ArticleAdmin(admin.ModelAdmin):
2     def save_model(self, request, obj, form, change):
3         obj.user = request.user
6     def queryset(self, request):
7         qs = self.model._default_manager.filter(user=request.user)
8         return qs


  • Much of ModelAdmin's functionality is a wrapper around ModelForm
  • If you can't do it in ModelAdmin, chances are ModelForm can help
  • Pulled directly from django.forms and no different in functionality

ModelForms Example

 1 class AuthorForm(forms.ModelForm):
 2     exclude_states = ['AS', 'GU', 'MP', 'VI',]
 3     def __init__(self, *args, **kwargs):
 4         # initalize form
 5         super(AuthorForm, self).__init__(*args, **kwargs)
 7         # rebuild choices
 8         w = self.fields['state'].widget
 9         choices = []
10         for key, value in w.choices:
11             if key not in self.exclude_states:
12                 choices.append((key, value))
13         w.choices = choices
15 class AuthorAdmin(admin.ModelAdmin):
16     form = AuthorForm

Caution: Here be Dragons

ModelAdmin/ModelForm Tips

  • The further you dig, the less documentation you'll find
  • Don't be afraid to study the source:
    • django.contrib.admin.sites.AdminSite
    • django.contrib.admin.options.ModelAdmin
    • django.forms.models.ModelForm
    • django.contrib.admin.options.InlineModelAdmin
    • django.forms.formsets
  • Use a debugger for sanity (ipdb.set_trace())

ModelAdmin/ModelForm Pros & Cons


  • Flexible
  • Powerful
  • No additional work to bundle with reusable apps


  • Gets complex quickly
  • May require getting familiar with undocumented Django internals

Custom Views

Just do it yourself

When in Doubt, Punt

Custom Views

  • The admin just wasn't built to do some things
  • Other things simply aren't worth the trouble
  • Build your own view and plug it into the admin

Custom View URL

 1 class PostAdmin(admin.ModelAdmin):
 2     def my_view(self, request):
 3         return admin_my_view(request, self)
 5     def get_urls(self):
 6         urls = super(PostAdmin, self).get_urls()
 7         my_urls = patterns('',
 8             (r'^my_view/$', self.my_view)
 9         )
10         return my_urls + urls

Custom View

 1 @permission_required('blog.add_post')
 2 def admin_my_view(request, model_admin):
 3     opts = model_admin.model._meta
 4     admin_site = model_admin.admin_site
 5     has_perm = request.user.has_perm(opts.app_label + '.' \
 6                                      + opts.get_change_permission())
 7     context = {
 8         'admin_site':,
 9         'title': "My Custom View",
10         'opts': opts,
11         'root_path': '/%s' % admin_site.root_path,
12         'app_label': opts.app_label,
13         'has_change_permission': has_perm
14     }
15     template = 'admin/demo_app/admin_my_view.html'
16     return render_to_response(template, context,
17               context_instance=RequestContext(request))

Custom View Template

 1 {% extends "admin/base_site.html" %}
 2 {% load i18n %}
 3 {% block breadcrumbs %}
 4 <div class="breadcrumbs">
 5     <a href="../../../">{% trans "Home" %}</a> &rsaquo;
 6     <a href="../../">{{ app_label|capfirst|escape }}</a> &rsaquo; 
 7     {% if has_change_permission %}<a href="../">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %} &rsaquo; My Custom View
 8 </div>
 9 {% endblock %}
11 {% block content %}
12     <!-- do stuff here -->
13 {% endblock %}

Custom View Example

Custom View Pros & Cons


  • More flexible
  • More powerful
  • No additional work to bundle with reusable apps


  • Can be tricky to integrate into workflow
  • You're on your own to validate forms, build templates, etc.


  • UI is important. Think about your end-user
  • Think about your users, not your database
  • The admin gives you hooks for lots of easy wins


Peter Baumgartner

Michael Trythall

Please rate this talk at:

Built with Landslide (