In a recent post Jacob Kaplan-Moss stated that writing a new Django admin would cost about $1M.
Fortunately for us most of the Django work has already been done, for free, as part of the Django REST Framework.
But what is Django admin anyways?
Before getting into the why's, what's and how's, let's first take a look at what we have.
Although it is over 10 years old, Django admin is still today one of the most brilliant and useful pieces of web software available. From just a Model definition and an extra line of code you get a fully featured admin interface on a database table with relationships and easy access rights management.
And with a few more lines of code you get fancy features like sorting, searching, filtering and more.
Although this is the kind of feature any developer or startup owner can dream for, very few other frameworks have attained the level of the Django admin, if I had to pick a next contender, I would name Laravel and its Laravel Administrator that have evolved from the first version of the Symfony framework which is about as old as Django.
Why a new admin then?
Django admin being the great piece of software it is, it also has its limitations. Some of these limitations are due to its age, others are there because they are great technical challenges and most of them are also due to the fact that the admin is a complex piece of software in which very few people have dared diving for the past ten years.
Personally, here is a list of some of the limitations I have run into when using Django admin.
Hardly skinnable
We have been told and re-told that the admin is not suited for end-users and that we should never expose the admin to them.
Probably one of the reasons we have been told that so many times is that most of us do it anyways. It is part of why people pick Django in the first place and even if, when becoming more mature, a lot of web applications will move away from the admin (because of its limitations), there are a lot of web applications out there exposing the admin to end-users.
As an interface exposed to end-users, a lot of people want to tweak its look-and-feel for various reasons.
Over time there have been several attempts to remedy this problem. To name just a few: grappelli, django-admin2 (although this one is not "just a skin") or django-admin-bootstrap. These libraries work fine in a limited use and fix the issues that lay in their initial scope but they are not any more flexible than the original Django admin.
Non-nestable
Let's say that you have 3 models: Company
, Employee
and ContactMechanism
. Each company has many employees which, in turn, have several contact mechanisms (phone, mobile, e-mail, skype, ...).
All you whish for is probably to be able to manage all that information in a single form. Unfortunately, this is not possible with Django admin because it only supports a single level of form-nesting. Once again several people have tried to tackle the problem and one of the most notable attempts is django-nested-admin. The problems with this kind of libraries are that
- they depend heavily on the HTML from the original admin and are susceptible to need a (partial) rewrite every time the HTML code of the admin changes
- to be usable with custom skinning libraries, libraries like django-nested-admin usually need a set of templates for each skinning library they want to be compatible with.
Mostly static
The last point I'll mention in this section is something that was perfectly fine 10 years ago but which, in the web of today, isn't anymore.
As an example, let's take the same models as above. A company has many employees but it also has a OneToOne
relationship with Employee
as default_contact.
In Django admin, on a company form with an inline form for employees and a ModelChoiceField
for default_contact, if you add a new employee in the inline form, you won't be able to select them as default contact in the admin right-away; you'll first have to save the whole form in order for the new employee to appear in the default_contact dropdown.
There are easy workarounds for that, like adding an is_default_contact
field to the Employee
model and using an @property
on Company
to access the default contact but it is what it is: a workaround.
What should this new admin do then?
Everyone probably have different use-cases for their project's admin interface but I believe that a new admin's alpha release should provide, at least, the following features:
- Ability to create a new
ModelAdmin
equivalent from a single line of code - Ability to create a
ModelAdmin
subclass equivalent to tweak at least the followinglist_display
fields
/fieldsets
filter_horiontal
/filter_vertical
search_fields
- An updated version of
form
- Same permissions scheme as the current admin using django.contrib.auth
- Single-record- as well as bulk-actions
- Fix most of the issues mentioned above
Ok, I see your point, but... how?
One thing is for sure, it's that Jacob Kaplan-Moss is right: If someone wanted to start a new admin from scratch to provide the above features (which are only a limited subset of what the current Django admin is capable of) it would cost a pot-load of money in man-hours.
But we are not starting from scratch! Although I'm sure the project's contributors still spent on lot of time on it, django-admin2 is an example of what can be accomplished by using other existing features of Django in order to build a new admin.
Also, re-creating a django admin as a set of server-rendered pages sounds like it will lead us to the same kinds of limitations in terms of staticness.
But, an admin being mostly built around models, if we are going to be using some frontend framework to build our admin, we need something that was built with this idea of models in mind. And if we want it to be easily skinnable and adaptable, we need a framework which is also built around templates.
For that part I picked EmberJS which fits all the requirements but I'm sure it can also be achieved using your favourite frontend framework.
If we are going to be building a new admin with a frontend framework, we will also need something to build a backend API. Whenever I hear the words "backend API" pronounced in the context of Django, Django REST Framework is the first thing that pops into my head. And that's a very good thing because DRF comes with a bunch of features that we are going to be able to leverage in the creation of this new admin, namely:
- permissions: the
permission_classes
property ofModelViewSet
's - filtering, searching and ordering
ModelSerializer
: our updated version ofform
- Single-record- and bulk-actions are available as part of the
ModelViewSet
(we may need to introducedjango-rest-framework-bulk
)
Now that we know what we already have, what is actually missing?
- A wrapper to automatically generate API endpoints from models. There is already a library that does that, the wq.db package provides an advanced ModelRouter class which is close to what we need but not close enough. We will non-the-less be able to leverage some of the ideas from that library.
- A way to customize the mentioned attributes on those endpoints
- A library to generate ember-data models from DRF serializers
- An ember-cli addon able to load those models as well as meta-data like
fieldsets
,fiter_horizontal
,filter_vertical
andsearch_fields
on the fly from Django - An ember-cli CRUD component addon able to generate a list view and a form view from an arbitrary model (and associated meta-data) and perform operations like filter and search.
What else could be gained from such an initiative?
This new admin would not be a "simple" project, it would actually be 2 interlinked projects:
- A Django backend serving data endpoints as well as meta data about those api endpoints
- An EmberJS frontend being able to perform auto-discovery on an API and then perform CRUD operations on the discovered API.
This means that, if successful, this could lead to some interesting cross-community projects that would enrich both sides of this new admin.
For example, someone using Ember and Phoenix could very well implement a Phoenix addon to serve an api compatible with the Ember-side of the project and, over time contribute to the frontend code.
Or someone using Django and Angular could as well decide to release a Angular-equivalent of the Ember-side of the project and contribute to the Django-side of the project.
It sounds easy! Why has no-one started yet?
If you really think I made it sound easy, I guess that's a good thing 😊. The idea behind this is nothing new or rare, it's actually the same principle that has been used by thin clients of 3-tiers applications (remember those?) for ages; including some well-known python ERP's.
I have actually been tinkering with the idea for quite a while now as a side project.
I've been doing a few spikes on all the points above and the reason I am only blogging about it now is because I will now start releasing Django and Ember libraries to achieve this goal.
The first library on that list is drf_auto_endpoint
.
Want to see more?
Here are some links to some WIP (but already working) libraries to fill the other gaps
djember_model
: a package to generate (on-the-fly, and soon on-disk) ember-data models from DRF serializersember-cli-dynamic-model
: an addon to dynamically load ember-data model definitions from outside of your main application code
You should also stay tuned to this blog where several other libraries will soon be published as well as a preview url for the full project.