5

django-admin 中的默认小部件ManyToManyFields很难使用。我可以设置filter_horizontal单个字段并获得更好的小部件。

如何将filter_horizontal所有设置为默认值ManyToManyFields

(当然,我也会很高兴filter_vertical。)

我四处寻找解决方案,但在 Google 或 SO 上没有找到任何东西。我可以考虑如何通过一些元编程来做到这一点,但如果有人已经这样做了,或者如果它在 Django 的某个地方,我很想听听。

4

2 回答 2

4

修改预先存在的代码中定义的类的最佳方法是使用mixin。需要修改类的formfield_for_manytomany方法ModelAdmin;该方法定义在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 日):

我完全了解子类化/继承的工作原理,这是解决此类问题的最佳实践。但是,正如我在下面的评论中重申的那样,子类化不会解决 OP 的问题,即。制作filter_horizontalfilter_vertical 默认。通过子类化,您不仅需要为所有模型注册子类,还必须取消注册在内置 Django 应用程序和已安装的第三方应用程序中注册的每个 ModelAdmin 子类,然后注册新的 ModelAdmin 子类。因此,例如对于 Django 的内置用户模型...

admin.site.unregister(User)
class CustomModelAdmin(admin.ModelAdmin):
    """ Add your changes here """
admin.site.register(User, CustomModelAdmin)

...然后为您安装的所有 Django 应用程序和第三方应用程序重复类似的代码。我认为这不是 OP 想要的,因此我的回答。

于 2012-01-02T12:43:17.403 回答
0

仍然有点hacky,但比其他答案更好,这会在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

由于某种原因,设置filter_horizontal不起作用__init__,并且没有get_filter_horizontal覆盖。

如上所述,避免猴子补丁,只继承自BaseClass. 当您在 6 个月后回到这里并且在继承层次结构中找不到它时,您,尤其是您的同事将不胜感激。

于 2019-08-27T14:09:17.517 回答