Django问题在ModelForms中继承formfield_callback

yel*_*ott 8 django django-forms

我现在只使用Django几周了,所以我可能会遇到各种各样的错误,但是:

我有一个基本的ModelForm,我把一些样板文件放在尽可能保持DRY的地方,而我所有的实际ModelForms只是将该基本形式子类化.这对我来说很有用error_css_class = 'error',required_css_class = 'required'但是formfield_callback = add_css_classes并没有像我期望的那样工作.

forms.py

# snippet I found
def add_css_classes(f, **kwargs):
    field = f.formfield(**kwargs)
    if field and 'class' not in field.widget.attrs:
        field.widget.attrs['class'] = '%s' % field.__class__.__name__.lower()
    return field

class BaseForm(forms.ModelForm):
    formfield_callback = add_css_classes  # not working

    error_css_class = 'error'
    required_css_class = 'required'
    class Meta:
        pass

class TimeLogForm(BaseForm):
    # I want the next line to be in the parent class
    # formfield_callback = add_css_classes
    class Meta(BaseForm.Meta):
        model = TimeLog
Run Code Online (Sandbox Code Playgroud)

最终目标是使用一类datefield/timefield/datetimefield在表单上打一些jquery日期时间选择器.我希望应用程序中的所有日期时间字段都使用相同的小部件,因此我选择这样做,而不是明确地为每个模型中的每个字段执行此操作.为每个表单类添加一个额外的行并不是什么大不了的事,但它只是在告诉我我无法理解它.在django来源中挖掘表明这可能正在做一些我不理解的事情:

django.forms.models

class ModelFormMetaclass(type):
    def __new__(cls, name, bases, attrs):
        formfield_callback = attrs.pop('formfield_callback', None)
Run Code Online (Sandbox Code Playgroud)

但我不知道如何__init__以及__new__所有的内部联系.在BaseForm中,我尝试__init__在调用super之前和之后覆盖并设置formfield_callback,但我猜它需要在args或kwargs中的某个地方.

ser*_*ach 1

__new__ 在对象构造之前调用。实际上这是一个工厂方法,它返回一个新构造的对象的实例。

所以ModelFormMetaclass中有 3 行关键行:

formfield_callback = attrs.pop('formfield_callback', None) #1
fields = fields_for_model(opts.model, opts.fields, 
                                      opts.exclude, opts.widgets, formfield_callback) #2
new_class.base_fields = fields #3
Run Code Online (Sandbox Code Playgroud)

在类中,我们将 base_fields 附加到我们的form中。

现在让我们看看ModelForm 类

class ModelForm(BaseModelForm):
    __metaclass__ = ModelFormMetaclass
Run Code Online (Sandbox Code Playgroud)

这意味着当我们创建 ModelForm 实例来更改未来实例的结构时,将调用 ModelFormMetaclass.__new__(...) 。ModelFormMetaclass中的__new__ ( def __new__(cls, name, bases, attrs) )的 attrs 是ModelForm class所有属性的字典。

因此,决定为我们的案例创建新的 InheritedFormMetaclass (从ModelFormMetaclass继承)。不要忘记在InheritedFormMetaclass中调用父级的new。然后创建我们的BaseForm 类并说:

__metaclass__ = InheritedFormMetaclass
Run Code Online (Sandbox Code Playgroud)

在InheritedFormMetaclass的 __new__(...) 实现中,我们可以做我们想做的一切。

如果我的回答不够详细,请通过评论告诉我。