The default widget for ManyToManyFields
in django-admin is difficult to use. I can set filter_horizontal
on individual fields and get a much nicer widget.
How can I set filter_horizontal
as the default on all ManyToManyFields
?
(I'd also be happy with filter_vertical
of course.)
I've searched around for a solution and didn't find anything on Google or SO. I can think of how to do this with some meta-programming, but if someone already did this or if it's in Django somewhere, I'd love to hear about it.
The best way to modify classes defined in pre-existing code is to use a mixin. You need to modify the formfield_for_manytomany
method of ModelAdmin
class; the method is defined in BaseModelAdmin
.
Add the following code in a module that's guaranteed to run when your Django server starts up [a models.py
of one of your own apps]:
from django.contrib.admin.options import ModelAdmin
from django.contrib.admin import widgets
class CustomModelAdmin:
def formfield_for_manytomany(self, db_field, request=None, **kwargs):
"""
Get a form Field for a ManyToManyField.
"""
# If it uses an intermediary model that isn't auto created, don't show
# a field in admin.
if not db_field.rel.through._meta.auto_created:
return None
db = kwargs.get('using')
if db_field.name in self.raw_id_fields:
kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel, using=db)
kwargs['help_text'] = ''
else:
kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, False) # change second argument to True for filter_vertical
return db_field.formfield(**kwargs)
ModelAdmin.__bases__ = (CustomModelAdmin,) + ModelAdmin.__bases__
Note (27 Aug 2019):
I'm fully aware of how subclassing/inheritance works, and that's best practice for solving problems like this. However, as I've reiterated in the comments below, subclassing will not solve the OP's problem as stated ie. making filter_horizontal
or filter_vertical
the default. With subclassing, not only will you need to register your subclass for all your models, you'll have to unregister each ModelAdmin subclass that's registered in builtin Django apps and third-party apps you've installed, then register your new ModelAdmin subclasses. So for example for Django's builtin User model ...
admin.site.unregister(User)
class CustomModelAdmin(admin.ModelAdmin):
""" Add your changes here """
admin.site.register(User, CustomModelAdmin)
... then repeat similar code for all Django apps and third-party apps you've installed. I don't think this is what the OP wanted, hence my answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With