关于python:定义ModelForm元类的正确方法

Correct way to define a ModelForm metaclass

我要做的是创建一个动态模型窗体,它基于要在ModelAdmin中使用的类属性生成额外的字段。比如:

1
2
3
4
5
class MyModelForm(forms.ModelForm):
    config_fields = ('book_type', 'is_featured', 'current_price__is_sale')

class MyModelAdmin(admin.ModelAdmin):
    form = MyModelForm

在这种情况下,MyModelForm将通过执行一些自省,基于config_fields属性生成字段。到目前为止,我的方法是这样的(基于这个答案https://stackoverflow.com/a/6581949/677985):

1
2
3
4
5
6
7
8
9
10
11
12
13
class ConfigForm(type):
    def __new__(cls, name, bases, attrs):
        if 'config_fields' in attrs:
            for config_field in attrs['config_fields']:

                # ... (removed for clarity)

                attrs.update(fields)
        return type(name, bases, attrs)

class MyModelForm(forms.ModelForm):
    __metaclass__ = ConfigForm
    config_fields = ('book_type', 'is_featured', 'current_price__is_sale')

这种方法非常有效,但我不太满意它,原因有几个:

  • 验证似乎不起作用,但目前这只是一个小问题。
  • 我不太清楚为什么在ATTRS中需要"if config"字段:"-条件,但它是
  • 我希望MyModelForm继承而不是设置uu元类uuuu属性,然后可以轻松地重用基类,并允许我轻松地重写clean和u init uuuuuu方法。
  • 我尝试实现第三个项目,结果是额外的字段没有显示在管理表单中。如果有人能帮我解决这个问题,或者至少能给我指明正确的方向,我将不胜感激。

    我知道使用一个元类来解决这个问题可能有点过头了,并且我猜想部分问题在于ModelForm的继承链中已经有一个或两个元类了。所以,如果有人有一个替代的解决方案可以达到同样的效果,那我也会很高兴的。


    这个怎么样,

    基本上,任何扩展您的stepForm的表单都会有您想要的元类,在下面的例子中,它是stepFormMetaClass,请注意,如果您在某个form.py文件中定义了表单,那么您需要在___init___.py中导入表单,以便在django启动序列中执行它。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    from django.forms.forms import DeclarativeFieldsMetaclass


    class StepFormMetaclass(DeclarativeFieldsMetaclass):
        .......
        def __new__(meta_class, name, bases, attributes):
            .....
            return DeclarativeFieldsMetaclass.__new__(meta_class, name, bases, attributes)

    class StepForm(six.with_metaclass(StepFormMetaclass, forms.Form, StepFormMixin)):
        def __init__(self, *args, **kwargs):

            super(StepForm, self).__init__(*args, **kwargs)


        def as_p(self):
            return ......


    我相信ModelForm已经有了一个元类,但是您通过设置自己的元类来覆盖它。这就是为什么你没有得到验证或者其他任何内置的模型形式的优点的原因。

    相反,您应该能够直接使用type创建您的ModelForm,它将描述您想要的类型,但仍然会导致modelforms元类完成它的工作。

    例子:

    1
    2
    3
    4
    5
    6
    7
        config_fields = ('book_type', 'is_featured', 'current_price__is_sale')
        # the below is an example, you need more work to construct the proper attrs
        attrs = dict((f, forms.SomeField) for f in config_fields)
        ConfigModelForm = type('DynamicModelForm', (forms.ModelForm,), attrs)

        class MyModelAdmin(admin.ModelAdmin):
            form = ConfigModelForm

    如果需要,可以将第一部分包装在函数中,并在ModelAdmin中为表单属性调用它。

    有关使用类型的链接和讨论,请参阅此处的答案。