Django app that provides an easy way of extending or overriding behaviour of ModelAdmin classes that have already been registered by other apps.
This is usually useful when the
ModelAdmin you're altering is part of
a third-party app that you can't/wouldn't want to fork.
Extending model admins
Let's assume we have a django project which uses
Most of the times you have a many-to-many relation you would like your
selection field to use filter_horizontal.
However, django.contrib.auth.admin.UserAdmin uses the simple
<select multiple> which
is incredibly annoying to use.
To be able to use
UserAdmin you can use django-admin-extend
in the admin module of one of the apps you write:
# admin.py from django.contrib.auth.models import User @extend_registered class ExtendedUserAdmin(registered_modeladmin(User)): filter_horizontal = ('user_permissions', 'groups')
You can also override a ModelAdmin's ModelForm:
# admin.py from django.core.exceptions import ValidationError @extend_registered class ExtendedUserForm(registered_form(User)): def clean_username(self): username = self.cleaned_data.get('username', None) if username and not username.isalpha(): raise ValidationError('Invalid username. Only alphabetic characters allowed') return username
The advantage of using
registered_modeladmin over explicitly inheriting from
ModelAdmin is the fact that multiple apps can override
functionality and remain decoupled.
Note: The order of in the
INSTALLED_APPS setting matters.
The app that uses
extend_registered needs to be after the app that first
defines and registers the
The alternative would be to use explicit inheritance:
# app1.admin class App1UserAdmin(UserAdmin): pass
# app2.admin class App2UserAdmin(App1UserAdmin): pass
But this creates a dependency between
Bidirectional many to many fields
This is a generic mechanism for implementing an old feature request that never got into django.
Let's assume you're using
django.contrib.sites and one of your models (let's say
Snippet) has a many to many relation with the
Site model. Whenever you add
a new site, you want to be able to assign snippets to that new site.
You can do this by using
# models.py class Snippet(models.Model): name = models.CharField(unique=True, max_length=255) sites = models.ManyToManyField(Site, null=False, blank=True)
# admin.py @extend_registered class ExtendedSiteAdminForm(add_bidirectional_m2m(registered_form(Site))): snippets = ModelMultipleChoiceField( queryset=Snippet.objects.all(), widget=FilteredSelectMultiple() ) def _get_bidirectional_m2m_fields(self): return super(ExtendedSiteAdminForm, self).\ _get_bidirectional_m2m_fields() + [('snippets', 'smartsnippet_set')]
_get_bidirectinal_m2m_fields needs to return a list of tuples, where each
tuple contains the form field name and the related manager's name.
Mind the fact that if the object you're saving is new, then the form's
method will cause a database save regardless of the value of the commit parameter.
The extension above can also be made from multiple apps, each injecting their own bidirectional many to many fields.
See bidirectional_many_to_many.png for an example of how this would look.