Pull to refresh

Django ModelAdmins autoregister

Reading time4 min
Views2.9K

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 ModelAdminon 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.

Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
Rating0
Comments3

Articles