Mon, Jun 18, 2012
A while back I had a Django application in which I needed registered users able to view, create, update and delete objects in my system. These objects were instances of only a subset of all the Django
models.Model subclasses I had defined in the models.py file of my application.
You may find this problem very similar to what th Django Admin site solves for administrators: you register some models that are displayed, and then the admins can create/update/delete the objects in the system as needed. Cool, lets use the Django admin! We should only call
admin.site.register() on those models we want users to be able to act on and we have a full CRUD interface over the models. However, there are some issues with this naive approach:
So, should we copy every template and replicate the logic of the Django Admin interface? No need. Fortunately, we can have multiple admin sites functioning at the same time.
The Django documentation explains that the default Admin site (
django.contrib.admin.site) is actually just an instance of
django.contrib.admin.sites.AdminSite. We can easily subclass
AdminSite class to build new instances of the Django administration site, provided we give it a name different than "admin" when we instantiate it. So, having our own custom admin is as easy as having code like this in an
August 2018: Please note that this post was written for an older version of Django. Changes in the code might be necessary to adapt it to the latest versions and best practices.
And in the urls.py:
Unfortunately a problem still persists, since only staff members are allowed to login into our new
UserAdmin site. For this, we inspect the django/contrib/admin/sites.py file and find all the checks for
is_staff. We find only one match in the
has_permission() function of the
AdminSite class itself. So in our subclass, we override the function removing the check:
Done? Not yet. If you try it out you will see that non staff users still can't login. Further inspection finds a
login() function in the
AdminSite class, and this chunk of code in it:
So as we are not providing any custom
login_form, it uses the default
AdminAuthenticationForm which must contain the check for
is_staff. Thanks to Django's modular architecture it is very easy to just make our own login form and plug it in our
UserAdmin. We just subclass
AdminAuthenticationForm and provide our own
clean() function, which works the same but avoids the check:
And we plug it in our UserAdmin class:
(Note aside: with this knowledge we could even create a different admin site for Django's superuser and regular staff members, just adding the
is_superuser check to
Now we are done! We can login successfully into our brand new admin (or not so much) site for non staff users. There are a few gotchas, namely that some of the templates used by the admin site also contains an
is_staff check in the template itself. For example in Django 1.3.1 line 26 of contrib/admin/templates/admin/base.html is:
so, for non-staff users, it won't show the logout bar at the top.
To make it really usable we must copy these templates, edit them, and include them in our application. This is really easy to do, and the documentation shows how you can override the templates for our
UserAdmin site only. This is more boring than the rest, so I'll leave it as homework :-)
© 2023. All rights reserved.