从this question我想将我的表单从常规Form
转换为ModelForm
,以便我可以利用instance
中的ModelForm
参数。
这是我目前的表单代码:
class OrderDetailForm(forms.Form):
def __init__(
self,
user,
can_edit_work_type=None,
can_edit_vendor=None,
can_edit_note=None,
*args,
**kwargs
):
super(OrderDetailForm, self).__init__(*args, **kwargs)
if can_edit_work_type:
self.fields['work_type'] = forms.ChoiceField(choices=Order.WORK_TYPE_CHOICES)
if can_edit_vendor:
self.fields['vendor'] = forms.ModelChoiceField(
queryset=Vendor.objects.all(),
empty_label="Choose a vendor",
)
if can_edit_note:
self.fields['note'] = forms.CharField(widget=forms.Textarea)
def clean(self):
super(OrderDetailForm, self).clean()
if 'note' in self.cleaned_data:
if len(self.cleaned_data['note']) < 50:
self._errors['note'] = self.error_class([u"Please enter a longer note."])
del self.cleaned_data['note']
return self.cleaned_data
正如您所看到的,我有一些if
语句可以确定字段是否在表单中显示(逻辑上它意味着某些用户只能编辑字段的某些部分)。
我如何在ModelForm
中执行此操作?我理解fields
是一个元组,因此无法像Form
中那样添加。所以我想做一些像
class OrderDetailForm(forms.ModelForm):
class Meta:
model = Order
# fields = ('work_type', 'vendor', 'note') I can't do that since I need to be able to control it. See below.
# Can I control widgets even if that field doesn't exist?
widgets = {
'note': forms.Textarea(),
}
def __init__(
self,
user,
can_edit_work_type=None,
can_edit_vendor=None,
can_edit_note=None,
*args,
**kwargs
):
super(OrderDetailForm, self).__init__(*args, **kwargs)
fields = []
if can_edit_work_type:
fields.append('work_type')
if can_edit_vendor:
fields.append('vendor')
if can_edit_note:
fields.append('note')
self.Meta.fields = tuple(fields) # Does this work?
def clean(self):
super(OrderDetailForm, self).clean()
if 'note' in self.cleaned_data:
if len(self.cleaned_data['note']) < 50:
self._errors['note'] = self.error_class([u"Please enter a longer note."])
del self.cleaned_data['note']
return self.cleaned_data
这可能吗?如何控制ModelForm
中的字段?
答案 0 :(得分:5)
另一种可能的方法是在视图中生成内联表单类,以根据请求排除字段,例如,为Order模型定义一个普通模型表单,称为OrderDetailForm:
class OrderDetailForm(forms.ModelForm):
class Meta:
model = Order
fields = ('work_type', 'vendor', 'note')
widgets = {
'note': forms.Textarea(),
}
在视图中,例如,编辑顺序,根据OrderDetailForm创建自定义表单:
def edit(request, order_id):
order = Order.objects.get(pk=order_id)
can_edit_work_type = bool(request.REQUEST.get('can_edit_work_type', False))
can_edit_vender = bool(request.REQUEST.get('can_edit_vender', False))
can_edit_note = bool(request.REQUEST.get('can_edit_note', False))
exclude_fields = []
if not can_edit_work_type:
exclude_fields.append('work_type')
if not can_edit_vender:
exclude_fields.append('vender')
if not can_edit_note:
exclude_fields.append('note')
class CustomizedOrderForm(OrderDetailForm):
class Meta:
model = Order
exclude = tuple(exclude_fields)
if request.method == 'POST':
form = CustomizedOrderForm(instance=order, data=request.POST)
if form.is_valid():
form.save()
else:
form = CustomizedOrderForm(instance=order)
return render(request, 'order_form.html', {'form': form})
答案 1 :(得分:2)
ModelForm
api非常类似于常规Form
。优点是,除了默认小部件,实例kwarg和save方法之类的便利之外,您现在还可以获得model validation。
fields
attr
仍然是dict
- 就像。您可以看到由元类here构建的字段。然后,转移through inheritance并致电super()
中的BaseModelForm.__init__
,我们到达declared fields的deepcopy
,{{3} } SortedDict
。 Form
和ModelForm
这两个子类BaseForm
都很常见。
将字段放在originally中,然后按照原始__init__
中的方式添加字段。
以同样的方式清洁它们。
然后,您可以覆盖save
方法:您可以调用super()
来取回对象,然后根据需要处理cleaned_data
中的数据。
class OrderDetailForm(forms.ModelForm):
# regular fields, not based on bools
# ...
class Meta:
model = Order
exclude = ('work_type', 'vendor', 'note')
# or fields = (...other fields )
def __init__(
self,
user,
can_edit_work_type=None,
can_edit_vendor=None,
can_edit_note=None,
*args,
**kwargs,
):
super(OrderDetailForm, self).__init__(*args, **kwargs)
if can_edit_work_type:
self.fields['work_type'] = forms.ChoiceField(
choices=Order.WORK_TYPE_CHOICES)
if can_edit_vendor:
self.fields['vendor'] = forms.ModelChoiceField(
queryset=Vendor.objects.all(),
empty_label="Choose a vendor",
)
if can_edit_note:
self.fields['note'] = forms.CharField(widget=forms.Textarea)
def clean(self):
# I never call super() in clean .. do I? .. hmmm
# maybe I should or is sth magic going on?
# alternately,
# data = self.cleaned_data
# let's call super though
data = super(OrderDetailForm, self).clean()
if 'note' in data:
if len(data['note']) < 50:
# I raise a validation error so .is_valid() comes back False
# form.errors happens magically ...
raise forms.ValidationError("Not long enough ...")
return data
def save(self, *args, **kwargs):
data = self.cleaned_data
# maybe do some stuff here
# ...
# commit=True or commit=False could be important
order = super(OrderDetailForm, self).save(*args, **kwargs)
if 'note' in data:
order.note = data['note']
# ... do other stuff
# probably ...
order.save()
# respect how model forms work.
return order