如何防止Django Admin覆盖自定义字段的自定义窗口小部件?

时间:2018-12-05 22:23:41

标签: python django django-forms django-admin

我有一个模型字段,一个表单字段和一个我在应用程序中使用的小部件。代码的细节无关紧要。关键是该字段可以正确地以常规形式呈现,但在admin中会被覆盖。

以下是该字段的基本伪代码:

class SandwichWidget(forms.Widget):
    template_name = 'sandwichfield/widgets/sandwichfield.html'

    def __init__(self, attrs=None, date_format=None, time_format=None):
        widgets = (
            NumberInput(),
            Select(choices=FILLING_CHOICES),
            NumberInput(),
        )
        super(SandwichWidget, self).__init__(widgets, attrs)

    def decompress(self, value):
        if value:
            value = Sandwich(value)
            return [
                value.top,
                value.middle,
                value.bottom
            ]
        return [None, None, None]


class SandwichFormField(forms.MultiValueField):
    widget = SandwichWidget

    def __init__(self, input_date_formats=None, input_time_formats=None, *args, **kwargs):
        fields = (
            forms.IntegerField(),
            forms.CharField(),
            forms.IntegerField(),
        )
        super(SandwichFormField, self).__init__(fields, *args, **kwargs)


class SandwichField(models.PositiveIntegerField):

    def get_internal_type(self):
        return 'IntegerField'

    def formfield(self, *args, **kwargs):
        defaults={'form_class': SandwichFormField}
        defaults.update(kwargs)
        return super(SandwichField, self).formfield(*args, **defaults)

这显然是由于this line in django/contrib/admin/options.py所致,它指定models.IntegerField的替代应为widgets.AdminIntegerFieldWidget。由于models.PositiveIntegerField继承自models.IntegerField,并且由于line 181遍历了该字段的所有子类,因此似乎无法防止在admin中覆盖该小部件。

这是一个真正的问题,因为我在整个网站和整个管理员中都使用了这个自定义字段及其自定义小部件,并且我不想每次我都为override_fields输入自定义值想要使用该字段。理想情况下,开发人员应该能够使用自定义字段,而不必每次都提供自定义管理员。

当前,我继承自forms.PositiveIntegerField,因为从数据库存储和检索时,它是一个正整数,并且我想利用已经存在的所有编码来处理正整数值。 / p>

当前看来,仅 解决方案正在将我的字段更改为从models.Field继承,然后从以下位置复制并粘贴所有PositiveIntegerFieldIntegerField功能django代码。有其他替代方法吗?

当然,我总是可以让自己的formfield忽略发送给它的任何小部件,并始终使用自定义小部件,但这在我主动想要覆盖小部件时提出了一个问题,这绝对可以发生。我只是不想让管理员覆盖我的小部件。

2 个答案:

答案 0 :(得分:2)

试试这个(我从底部添加了第二行到你的代码中):

class SandwichField(models.PositiveIntegerField):

    def formfield(self, *args, **kwargs):
        defaults={'form_class': SandwichFormField}
        defaults.update(kwargs)
        defaults['widget'] = SandwichFormField.widget
        return super(SandwichField, self).formfield(*args, **defaults)

管理员代码传递给你一个 kwargs,它有自己的小部件,所以你有 覆盖它。

答案 1 :(得分:0)

在模型管理员中,您需要在formfield_overrides中添加这些小部件。例如这样的

class BaseModelAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.CharField: {'widget': forms.Textarea},
    }

现在,您可以从此类中对所有模型管理员进行子类化,而不必编写多余的代码来覆盖。