在django-admin中,如何将filter_horizontal设置为默认?
在django-admin中,ManyToManyFields
的默认小部件使用起来不太方便。我可以在每个字段上设置filter_horizontal
,这样就能得到一个更好用的小部件。
我想知道怎么能把filter_horizontal
设置成所有ManyToManyFields
的默认选项?
当然,如果能用filter_vertical
也可以。
我在网上找了找解决办法,但在谷歌和StackOverflow上都没有找到。我能想到用一些元编程的方法来实现,但如果有人已经做过这个,或者在Django里有相关的内容,我很想知道。
2 个回答
这个方法虽然还有点小技巧,但比其他答案要好,因为其他方法在升级Django的时候会带来很多麻烦。建议从这个方法继承(不要随便修改原有代码):
class BaseAdmin(models.Admin):
def formfield_for_manytomany(self, db_field, request=None, **kwargs):
"""
Get a form Field for a ManyToManyField. Tweak so filter_horizontal
control used by default. If raw_id or autocomplete are specified
will take precedence over this.
"""
filter_horizontal_original = self.filter_horizontal
self.filter_horizontal = (db_field.name,)
form_field = super().formfield_for_manytomany(db_field, request=None, **kwargs)
self.filter_horizontal = filter_horizontal_original
return form_field
@admin.register(AcmeModel)
class AcmeModelAdmin(BaseAdmin):
# Sub-classes can still specify raw_id, autocomplete, etc.
# That will override our filter_horizontal defaulting.
pass
不知道为什么在__init__
里设置filter_horizontal
不起作用,而且也没有get_filter_horizontal
可以重写。
正如上面提到的,尽量避免随便修改原有代码,直接从BaseClass
继承就好。等你过六个月再回来看这个代码时,你和你的同事一定会感激这样的做法,因为你们能更容易找到这个继承关系。
修改已经存在的代码中的类,最好的方法是使用一个叫做 mixin 的东西。你需要修改 ModelAdmin
类中的 formfield_for_manytomany
方法;这个方法在 BaseModelAdmin 中定义。
在一个确保在你的 Django 服务器启动时会运行的模块中添加以下代码 [可以是你自己应用的 models.py
]:
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__
注意 (2019年8月27日):
我完全知道子类化/继承是怎么回事,这通常是解决这类问题的最佳做法。然而,正如我在下面的评论中反复强调的,子类化 并不能解决提问者所说的问题,也就是让 filter_horizontal
或 filter_vertical
成为默认选项。使用子类化的话,不仅需要为所有模型注册你的子类,还得取消注册在 Django 内置应用和你安装的第三方应用中注册的每个 ModelAdmin 子类,然后再注册你新的 ModelAdmin 子类。举个例子,对于 Django 的内置用户模型 ...
admin.site.unregister(User)
class CustomModelAdmin(admin.ModelAdmin):
""" Add your changes here """
admin.site.register(User, CustomModelAdmin)
... 然后对你安装的所有 Django 应用和第三方应用重复类似的代码。我觉得这并不是提问者想要的,所以我给出了这样的回答。