我正在考虑创建一个mixin表单类,以便我可以将一组通用字段添加到各种非常不同的表单中。只是将它用作基类是行不通的,因为我希望能够使用其他形式作为基类,如下所示:
class NoteFormMixin(object):
note = forms.CharField()
class MainForm(forms.Form):
name = forms.CharField()
age = forms.IntegerField()
class SpecialForm(MainForm, NoteFormMixin):
favorite_color = forms.CharField()
我唯一的问题是:这是如何运作的?到目前为止看起来如果我使用mixin,那么它无法识别mixin中设置的字段:
>>> ff1 = SpecialForm()
>>> ff1.fields
{'name': <django.forms.fields.CharField object at 0x178d3110>, 'age': <django.forms.fields.IntegerField object at 0x178d3190>, 'favorite_color': <django.forms.fields.CharField object at 0x178d3210>}
这只是无法做到的事情吗?
答案 0 :(得分:23)
问题是你的NoteFormMixin派生自object而不是forms.Form。您需要将其更改为:
class NoteFormMixin(forms.Form):
note = forms.CharField()
答案 1 :(得分:6)
Patrick Altman的解决方案仅适用于常规表单 - 如果您使用ModelForm进行尝试,您将遇到元类冲突或某些字段缺失。
我找到的最简单和最简短的解决方案是附加到Django'ticket #7018 - 谢谢你, bear330 :o)
你需要:
from django.forms.forms import get_declared_fields
. . .
class ParentsIncludedModelFormMetaclass(ModelFormMetaclass):
"""
Thanks to bear330 - taken from https://code.djangoproject.com/attachment/ticket/7018/metaforms.py
"""
def __new__(cls, name, bases, attrs):
# We store attrs as ModelFormMetaclass.__new__ clears all fields from it
attrs_copy = attrs.copy()
new_class = super(ParentsIncludedModelFormMetaclass, cls).__new__(cls, name, bases, attrs)
# All declared fields + model fields from parent classes
fields_without_current_model = get_declared_fields(bases, attrs_copy, True)
new_class.base_fields.update(fields_without_current_model)
return new_class
def get_next_in_mro(current_class, class_to_find):
"""
Small util - used to call get the next class in the MRO chain of the class
You'll need this in your Mixins if you want to override a standard ModelForm method
"""
mro = current_class.__mro__
try:
class_index = mro.index(class_to_find)
return mro[class_index+1]
except ValueError:
raise TypeError('Could not find class %s in MRO of class %s' % (class_to_find.__name__, current_class.__name__))
然后你将mixin定义为通常的ModelForm,但是没有声明Meta :
from django import forms
class ModelFormMixin(forms.ModelForm):
field_in_mixin = forms.CharField(required=True, max_length=100, label=u"Field in mixin")
. . .
# if you need special logic in your __init__ override as usual, but make sure to
# use get_next_in_mro() instead of super()
def __init__(self, *args, **kwargs):
#
result = get_next_in_mro(self.__class__, ModelFormMixin).__init__(self, *args, **kwargs)
# do your specific initializations - you have access to self.fields and all the usual stuff
print "ModelFormMixin.__init__"
return result
def clean(self):
result = get_next_in_mro(self.__class__, ModelFormMixin).clean(self)
# do your specific cleaning
print "ModelFormMixin.clean"
return result
最后 - 最终的ModelForm,重用了ModelFormMixin的功能。 你应该定义Meta和所有常用的东西。在最终形式中你可以称为超级(...) 当你重写方法时(见下文)。
注意:最终格式必须将ParentsIncludedModelFormMetaclass设置为元类
注意:类的顺序很重要 - 将mixin放在第一位,然后放入ModelFrom。
class FinalModelForm(ModelFormMixin, forms.ModelForm):
"""
The concrete form.
"""
__metaclass__ = ParentsIncludedModelFormMetaclass
class Meta:
model = SomeModel
field_in_final_form = forms.CharField(required=True, max_length=100, label=u"Field in final form")
def clean(self):
result = super(FinalModelForm, self).clean()
# do your specific cleaning
print "FinalModelForm.clean"
return result
请记住,这只适用于两个类都是ModelForms的情况。如果你试图将Form和ModelFrom与这种技术混合搭配,它就不会很漂亮了:o)
答案 2 :(得分:2)
class TextFormMixin(object):
def __init__(self, *args, **kwargs):
super(TextFormMixin, self).__init__(*args, **kwargs)
self.fields['text'] = forms.CharField(widget=forms.Textarea, required=True)
def clean_text(self):
if not ('{{EMAIL}}' in self.cleaned_data.get('text', '')):
raise ValidationError("You have to put {{EMAIL}} in message body.")
return self.cleaned_data.get('text', '')
def get_text(self):
return self.cleaned_dat['text'].replace('{{EMAIL}}', self.case.get_email())
class NewCaseForm(TextFormMixin, forms.ModelForm):
pass
class ReplyForm(TextFormMixin, forms.Form):
to = forms.CharField(max_length=50)
subject = forms.CharField(max_length=50)
答案 3 :(得分:0)
只需澄清@Adam Dobrawy的回答,对我有帮助:
这不起作用:
class NoteFormMixin(object):
note = forms.CharField()
这样做:
class NoteFormMixin(object):
def __init__(self, *args, **kwargs):
super(NoteFormMixin, self).__init__(*args, **kwargs)
self.fields['note'] = forms.CharField()
此行为可能与django在类实例化期间收集字段的方式有关。.我没有费心去研究它的杂草。我只是发现这个花絮让我能够以一种易于阅读的方式编写我的mixin,而无需任何额外的特定于Django形式的操作。