4

この質問から、フォームを通常からに変換して、FormのパラメータModelFormを利用できるようにします。instanceModelForm

これが私の現在のフォームコードです:

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か?

4

2 に答える 2

5

別の可能な方法は、ビューでインラインフォームクラスを生成して、リクエストに基づいてフィールドを除外することです。たとえば、OrderDetailFormと呼ばれるOrderモデルの通常のモデルフォームを定義します。

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})
于 2012-05-18T04:04:21.493 に答える
2

ModelFormAPIは通常のAPIと非常によく似ていFormます。利点は、デフォルトウィジェット、インスタンスkwarg、saveメソッドなどの便利な機能に加えて、モデルの検証ができることです。

fields attrまだdictのようです。ここで、メタクラスによって構築されるフィールドを確認できます。次に、継承を経て、呼び出すと、宣言されたフィールドの1つ、元々はに到達します。これは、と、の両方のサブクラスに共通です。super()BaseModelForm.__init__deepcopySortedDictFormModelFormBaseForm

フィールドを除外に入れて、元のフィールドと同じように追加します__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
于 2012-05-18T03:13:23.703 に答える