【问题标题】:Validating a formset using data from another form使用来自另一个表单的数据验证表单集
【发布时间】:2020-01-29 09:23:03
【问题描述】:

我必须对表单和表单集进行一些验证。表格中的 £££ 金额必须等于表格集中金额的总和。

经过大量谷歌搜索后,我找到了一个解决方案,我将自定义 init 添加到基本表单集,如下所示:

class BaseSplitPaymentLineItemFormSet(BaseFormSet):
    def __init__(self, cr=None, *args, **kwargs):
        self._cr = cr
        super().__init__(*args, **kwargs)
    def clean(self):
        if any(self.errors):
            return
        sum_dr = 0
        for form in self.forms:
            sum_dr += form.cleaned_data.get('dr')
        if sum_dr != float(self._cr):
            raise forms.ValidationError('The amount entered needs to equal the sum of the split payments.')

然后我在实例化表单集时从表单中传递金额值,以便可以在表单集验证中使用该值:

lineitem_formset = LineItemFormSet(form.data['amount'], request.POST)

这对于使用 formset_factory() 的 create_new 视图非常有用。今天早上我使用 inline_formsetfactory() 编写了更新视图,但现在我得到一个错误:

__init__() got an unexpected keyword argument 'instance'

我对自定义 init 的工作原理只有一个基本的了解,因此我找不到解决此错误的方法。

Forms.py:

class SplitPaymentForm(forms.Form):
    date = forms.DateField(widget=DateTypeInput())
    account = GroupedModelChoiceField(queryset=Ledger.objects.filter(coa_sub_group__type='a').order_by('coa_sub_group__name','name'), choices_groupby = 'coa_sub_group')
    store = forms.CharField(required=True)
    amount = forms.DecimalField(decimal_places=2)

class SplitPaymentLineItemForm(ModelForm):
    ledger = GroupedModelChoiceField(queryset=Ledger.objects.all().order_by('coa_sub_group__name', 'name'), choices_groupby = 'coa_sub_group', empty_label="Ledger", required=True)
    project = forms.ModelChoiceField(queryset=Project.objects.filter(status=0), empty_label="Project", required=False)
    class Meta:
        model = LineItem
        fields = ['description','project', 'ledger','dr',]
    # This init disallows empty formsets
    def __init__(self, *arg, **kwarg):
            super(SplitPaymentLineItemForm, self).__init__(*arg, **kwarg)
            self.empty_permitted = False


class BaseSplitPaymentLineItemFormSet(BaseFormSet):
    def __init__(self, cr=None, *args, **kwargs):
        self._cr = cr
        super().__init__(*args, **kwargs)
    def clean(self):
        if any(self.errors):
            return
        sum_dr = 0
        for form in self.forms:
            sum_dr += form.cleaned_data.get('dr')
        if sum_dr != float(self._cr):
            raise forms.ValidationError('The amount entered needs to equal the sum of the split payments.')

Views.py:

def split_payments_new(request):
    LineItemFormSet = formset_factory(SplitPaymentLineItemForm, formset=BaseSplitPaymentLineItemFormSet, extra=2)
    if request.method == 'POST':
        form = SplitPaymentForm(request.POST)
        lineitem_formset = LineItemFormSet(form.data['amount'], request.POST)
        if form.is_valid() and lineitem_formset.is_valid():
            q0 = JournalEntry(user=request.user, date=form.cleaned_data['date'], type="SP",)
            q1 = LineItem(journal_entry=q0, description=form.cleaned_data['store'], ledger=form.cleaned_data['account'], cr=form.cleaned_data['amount'])
            q0.save()
            q1.save()
            for lineitem in lineitem_formset:
                q2 = LineItem(journal_entry=q0,description=lineitem.cleaned_data.get('description'),ledger=lineitem.cleaned_data.get('ledger'),project=lineitem.cleaned_data.get('project'),dr=lineitem.cleaned_data.get('dr'))
                q2.save()
            messages.success(request, "Split payment successfully created.")
            return HttpResponseRedirect(reverse('journal:split_payments_show_detail', kwargs={'pk': q0.id}) )
    else:
        form = SplitPaymentForm(initial = {'date': datetime.date.today().strftime('%Y-%m-%d')})
        lineitem_formset = LineItemFormSet()
    return render(request, 'journal/split_payments_new.html', {'form': form, 'formset': lineitem_formset})

def split_payments_update(request, pk):
    journal_entry = get_object_or_404(JournalEntry, pk=pk, type="SP")
    lineitem = LineItem.objects.get(journal_entry=journal_entry.id, dr__isnull=True)

    initial = {
        'date': journal_entry.date.strftime('%Y-%m-%d'),
        'account': lineitem.ledger,
        'store': lineitem.description,
        'amount': lineitem.cr,
    }
    form = SplitPaymentForm(initial=initial)

    LineItemFormSet = inlineformset_factory(JournalEntry, LineItem, form=SplitPaymentLineItemForm, formset=BaseSplitPaymentLineItemFormSet, extra=0)
    lineitem_formset = LineItemFormSet(instance=journal_entry)

    if request.method == 'POST':
        lineitem_formset = LineItemFormSet(form.data['amount'], request.POST, instance=journal_entry)
        form = SplitPaymentForm(request.POST)
        if lineitem_formset.is_valid() and form.is_valid():
            lineitem_formset.save()
            journal_entry.date = form.cleaned_data['date']
            lineitem.ledger = form.cleaned_data['account']
            lineitem.description = form.cleaned_data['store']
            lineitem.cr = form.cleaned_data['amount']
            journal_entry.save()
            lineitem.save()
            messages.success(request, "Split payment successfully updated.")
            return HttpResponseRedirect(reverse('journal:split_payments_show_detail', kwargs={'pk': journal_entry.id}) )
    return render(request, 'journal/split_payments_update.html',{'form': form, 'formset': lineitem_formset, 'journal_entry': journal_entry})


【问题讨论】:

  • 当您更改为inlineformset 时,您是否检查了Django 源代码以查看内联表单集是否使用相同的BaseFormSet?我的猜测是他们使用不同的基础,可能是 BaseInlineFormSet,具有不同的初始化签名。

标签: django django-forms


【解决方案1】:

解决了。只需要使用 BaseInlineFormSet。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-18
    • 2016-08-10
    • 2011-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-30
    相关资源
    最近更新 更多