Some time ago I discovered that Django has the ability to auto-register ModelAdmins
. Since this is not common knowledge and carries a number of benefits, I decided to write an article about it to bring it to the attention of the Django community.
First, let's go over the basics of how to normally register ModelAdmin
.
Standard ModelAdmin registration
To register ModelAdmin
, we first create a model
from django.db import models
class Product(models.Model):
title = models.CharField(max_length=255)
Next you need to create a ModelAdmin
and register it with the model on the SiteAdmin
.
from django.contrib import admin
from .models import Product
@admin.register(Product)
class ProductModelAdmin(admin.ModelAdmin):
fields = ('title')
Now let's run our dev server and see if it worked.
But what are these? Some additional ModelAdmins
are registered, but we've never defined them. This means Django is auto-registering these standard ModelAdmins
somewhere.
Django ModelAdmins integrated auto-register.
After some digging in django.contrib.admin.apps
, I found that these ModelAdmins
are registered in the ready
method of the AdminConfig
class.
class AdminConfig(SimpleAdminConfig):
"""The default AppConfig for admin which does autodiscovery."""
default = True
def ready(self):
super().ready()
self.module.autodiscover()
We can use this fact to our advantage if we wrap this method not only for auto-registering the standard Django models, but also for our own.
We can do this as follows:
from django.contrib.admin.apps import AdminConfig as BaseConfig
from django.apps import apps
from django.contrib.admin.sites import site
class AdminsConfig(BaseConfig):
def ready(self, *args, **kwargs):
super().ready(*args, **kwargs)
for config in apps.get_app_configs():
admin_module = getattr(config.module, 'admin', None)
for model in config.get_models():
if not site.is_registered(model):
modeladmin = getattr(admin_module, f'{model.__name__}ModelAdmin', None)
site.register(model, modeladmin)
We go through all AppConfigs
in our project and get the admin module from AppConfigs
. Of course, this is the case if the admin module exists.
We then go through all models from this AppConfig
.
There we check if the model has already been registered with SiteAdmin
.
If not, we search for ModelAdmin
class with name 'model_nameModelAdmin' in the admin module. If no such class is defined, we use None
.
Finally, we register the model using the found ModelAdmin
on SiteAdmin
.
If ModelAdmin
is not defined, the register
method registers the model to ModelAdmin
class by default.
But why should you do it this anyway?
Benefits
1. Shorter Code
With this approach to registering ModelAdmins
you can multiple lines of code in one 10-line solution.
You can say goodbye to the empty ModelAdmin
classes just to register the model, if you done it before.
No more @admin.register(model)
wrappers on any ModelAdmin
.
Most importantly, no more long import lists for your models in the module admin.
Depending on how many models you have and how many ModelAdmins
need to be registered you can save a lot of lines of code.
2. Easier development
Continuing with the idea of the previous point, you no longer need to worry about registering ModelAdmin
during development because everything happens automatically when the server starts up.
3. Circular Imports
With the current system with huge import lists in the admin module, it is not that difficult to get circular imports and find a workaround. With this solution, these import lists are gone and you no longer have to worry about models and circular import.
Variations
Sometimes you don't want register on the current SiteAdmin
all of your models, so what do you do then?
Of course you can modify the solution above as you wish, but here I want to give you some ideas for solving the most common problems.
Register specific models only
If we want to register only the required models with ModelAdmins
, we can define only the required ModelAdmins
in the admin module and do the following:
def ready(self, *args, **kwargs):
super().ready(*args, **kwargs)
for config in apps.get_app_configs():
admin_module = getattr(config.module, 'admin', None)
for model in config.get_models():
if not site.is_registered(model):
modeladmin = getattr(admin_module, f'{model.__name__}ModelAdmin', None)
if modeladmin:
site.register(model, modeladmin)
In this solution you need to add in the admin module **ModelName**ModelAdmin
class for each required model.
Alternatively you can add a flag to the model if it should be auto-registered.
# author: Martin Achenrainer
def ready(self, *args, **kwargs):
super().ready(*args, **kwargs)
for config in apps.get_app_configs():
admin_module = getattr(config.module, 'admin', None)
for model in config.get_models():
if not site.is_registered(model):
modeladmin = getattr(admin_module, f'{model.__name__}ModelAdmin', None)
if modeladmin or hasattr(mode, 'autoregister_model'):
site.register(model, modeladmin)
Remove standard registered models
Perhaps you don't need the standard Django models registered in your project. You can remove them with one simple line added to the ready method.
def ready(self, *args, **kwargs):
super().ready(*args, **kwargs)
site._registry = []
These are just some of the options for using Django autoregister. I'm sure you can think of other clever ways to use it, and I hope this article was useful.
I thank Martin Achenrainer, who is an apprentice at my firm, for translating and assisting with the English article. The idea was first presented in September 2021 at PyCon RU
Some words from Martin Achenrainer.
I would like to thank Maxim Danilov and his company wP soft for the opportunity to learn and improve my programming skills in the workplace.