1

我想要做的是创建一个动态的 ModelForm,它会根据它的一个类属性生成额外的字段,以在 ModelAdmin 中使用。就像是:

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):

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')

这种方法效果很好,但我对它不太满意,原因如下:

  1. 验证似乎不起作用,但目前这是一个小问题
  2. 我不太确定为什么需要“if config_field in attrs:”-condition,但它是
  3. 我希望 MyModelForm 继承而不是设置 __metaclass__ 属性,然后可以轻松重用基类,并允许我轻松覆盖 clean- 和 __init__- 方法。

我尝试实现第三项,结果是额外字段没有出现在管理表单中。如果有人可以帮助我解决这个问题,或者至少为我指出正确的方向,我将不胜感激。

我知道为此使用元类可能有点矫枉过正,并且猜测问题的一部分是 ModelForm 在其继承链中已经有一个或两个元类。因此,如果有人有替代解决方案来完成相同的任务,那会让我同样高兴。

4

2 回答 2

0

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

相反,您应该能够type直接使用来创建您的ModelForm,它将描述您想要的类型,但仍然会导致 ModelForms 元类做它的事情。

例子:

    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 中为您的表单属性调用它。

有关使用类型的链接和讨论,请参见我的答案。

于 2013-02-01T13:31:34.313 回答
0

这个怎么样,

基本上,任何扩展您的 StepForm 的表单也将具有您想要的元类,在下面的情况下它是 StepFormMetaclass,请注意,如果您在某个 form.py 文件中定义了表单,您将需要导入表单,___init___.py以便它将在 django 启动序列期间执行它。

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 ......
于 2014-04-22T15:56:19.463 回答