David
David

Reputation: 151

How to create an admin mixin for common/abstract model in Django

I am creating a multi tenant app and have the following abstract class that all relevant tenant specific models inherit from:

class TenantAwareModel(models.Model):
    tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE)

    class Meta:
        abstract = True

When registering models in the Django admin, I want to keep my code DRY and not have to add 'tenant' to every single search_fields, list_display, list_filter etc so I tried creating a super class to inherit from.

class TenantAwareAdmin(admin.ModelAdmin):
    class Meta:
        abstract = True

    # change_form_template = "admin/professional_enquiry_change_form.html"
    search_fields = ('tenant__id', 'tenant__name', 'tenant__domain',)
    list_display = ('tenant',)
    list_filter = ('tenant',)

And then trying to inherit that in the registration of the other models. E.g.

class UserAdmin(TenantAwareAdmin, admin.ModelAdmin ):
  ...

This approach matches the way the TenantAwareModel works.

class TenantAwareModel(models.Model):
    tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE)

    class Meta:
        abstract = True

class User(AbstractUser, Uuidable, Timestampable, TenantAwareModel):
   ...

and it is of course giving me:

django.core.exceptions.ImproperlyConfigured: The model TenantAwareModel is abstract, so it cannot be registered with admin.

Does anybody know the proper way to do this to avoid all the duplicated code?

Upvotes: 2

Views: 1397

Answers (1)

SergioC
SergioC

Reputation: 159

The Meta class and abstract=True can only be defined in the model definition, not in the ModelAdmin.

The TenantAwareModel is an abstract model that allows you to avoid repeating the common fields in the definition of others models, but you can't register a abstract model in the admin site. The abstract model is not related with any table, its only task is to be a base from which inherit in other models that will represent tables(not abstract models) and which will be able to be registered in the admin site.

To avoid repeat the same fields in search_fields, list_display, etc, you should create a mixin like this:

class TenantAwareAdminMixin:
    search_fields = [..., ...]
    list_display = [..., ...]
    ...

and inherit from this

class UserAdmin(TenantAwareAdminMixin, admin.ModelAdmin):
    ...

But there is a problem... If you override some field defined in the TenantAwareAdminMixin, this will overwrite the value provided by the mixin forcing you to explicitly add the inherited values plus those new values, like this:

class UserAdmin(TenantAwareAdminMixin, admin.ModelAdmin):
    list_display = TenantAwareAdminMixin.list_display + ['new_value']
    ...

It is possible to obtain a similar result by overriding the get_list_display, etc. methods.

Upvotes: 3

Related Questions