Django:如何针对另一个相关表单验证inlineformset

时间:2015-11-03 18:03:47

标签: django

我已经看了很多,但不能弄清楚如何使这项工作。我在视图中基本上有一个Document表单和一个Item inlineformset,我需要根据每个表单中的字段值执行一些验证。例如,如果项目的copyright_needed字段为YES,则需要Document的帐户字段。

如何传递对Document表单的引用,以便在ItemForm的clean方法中,我可以查看Document表单的cleaning_data?我尝试使用咖喱,正如我在其他SO答案中看到的那样,但是它的工作做得并不正确。

Models.py

class Document(models.Model):
   account = models.CharField(max_length=22, blank=True, null=True)

class Item(models.Model):
    copyright_needed = models.CharField(max_length=1)
    # Document foreign key
    document = models.ForeignKey(Document)

它是ItemForm清洁方法,显示了我想要完成的事情,以及我得到的错误。

Forms.py - 编辑 - 将init添加到ItemForm

class ItemForm(forms.ModelForm):
    class Meta:
        model = Item
        fields=[..., 'copyright_needed' ]
    def __init__(self, *args, **kwargs):
        self.doc_form = kwargs.pop('doc_form')
        super(ItemForm, self).__init__(*args, **kwargs)
    def clean(self):
        cleaned_data = super(ItemForm, self).clean()
        msg_required = "This field is required."

        cr = cleaned_data.get("copyright_needed")
        # This line generates this error: DocumentForm object has no attribute cleaned_data
        acct_num = self.doc_form.cleaned_data.get("account")
        if cr and cr == Item.YES:
            if not acct_num:
                self.doc_form.add_error("account", msg_required)
        return cleaned_data


class DocumentForm(forms.ModelForm):
      ...
      account = forms.CharField(widget=forms.TextInput(attrs={'size':'25'}), required=False)
    class Meta:
        model = Document
        fields = [ ..., 'account' ]

views.py

def create_item(request):
    # create empty forms
    form=DocumentForm()
    ItemFormSet = inlineformset_factory(Document, Item, 
        form=ItemForm,
        can_delete=False,
        extra=1 )
    # This is my attempt to pass the DocumentForm to each ItemForm, but its not working
    ItemFormSet.form = staticmethod(curry(ItemForm, doc_form=form)) 
    item_formset=ItemFormSet(instance=Document())
    if request.POST:

        d = Document()
        form=DocumentForm(request.POST, instance=d)
        if form.is_valid():
            new_document=form.save(commit=False)
            item_formset=ItemFormSet(request.POST, instance=new_document)
            if item_formset.is_valid():
                new_document.save()
                new_item=item_formset.save()
                    return HttpResponseRedirect(...)
        item_formset=ItemFormSet(request.POST)
    return render(request,...)

1 个答案:

答案 0 :(得分:1)

我甚至不确定视图的作用 - 看起来你对inlineformset和curry的作用感到困惑。首先,您使用doc_form来讨论ItemForm的init方法,但是您还没有编写init。

其次,看起来您希望能够编辑“文档”表单中的“项目”。因此,您需要modelformset_factory,并传入一个自定义Formset,您可以在其上编写一个干净的方法,该方法可以访问您需要的所有内容。

from django.forms.models import modelformset_factory

ItemFormSet = modelformset_factory(Item, form=ItemForm, formset=MyCustomFormset)

然后在你的customformset中 -

class MyCustomFormset(BaseInlineFormset):

    def clean():
        super(MyCustomFormset, self).clean()
        for form in self.forms:
            #do stuff

注意每个ItemForm上的clean方法已被调用 - 这类似于在普通模型上编写自己的clean()。

编辑:

好的,所以忽略了formset干净,我误解了。只需在视图中创建文档表单,将其与表单集一起传递,然后将它们全部放在相同的表单标记中。

<form method="post" action=".">
    {%for field in doc_form %}
         {{field}}
    {%endfor%}
    {%for form in formset%}
        {{form.as_p}}
    {%endfor%}
</form>

然后您可以访问request.POST中的所有字段,您可以随心所欲地执行任何操作

doc_form = DocumentForm(request.POST)
formset = ItemFormSet(request.POST)
if all([doc_form.is_valid(), formset.is_valid()]):
     #do some stuff