如何在Django中以编程方式设置ModelForm中的字段?

hob*_*es3 3 python django-forms

这个问题我想从常规转换我的表单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
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我有一些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
Run Code Online (Sandbox Code Playgroud)

那可能吗?你如何控制领域ModelForm

小智 5

另一种可能的方法是在视图中生成内联表单类,以根据请求排除字段,例如,为Order模型定义一个名为OrderDetailForm的正常模型表单:

class OrderDetailForm(forms.ModelForm):
    class Meta:
        model = Order
        fields = ('work_type', 'vendor', 'note') 
        widgets = {
            'note': forms.Textarea(),
        }
Run Code Online (Sandbox Code Playgroud)

在视图中,例如,编辑订单,根据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})
Run Code Online (Sandbox Code Playgroud)