Ошибка MultiValueDictKeyError при добавлении строк с помощью inlineformset

#django #inline-formset

#django #встроенный-formset

Вопрос:

Я пытаюсь добавить больше строк на встроенную фабрику наборов форм, используя ту же методологию, которую я использовал ранее на фабрике наборов форм, но выдает ошибку:

 MultiValueDictKeyError form-TOTAL_FORMS'
 

models.py:

     class ttransactions(models.Model):
    transaction_type = models.CharField(max_length=10, choices=tx_choices)
    description = models.CharField(max_length=50, null=False, blank=False, default='Description')
    transaction_date = models.DateField(default=datetime.today, db_index=True)
    company = models.ForeignKey(tcompany, on_delete=models.PROTECT, db_index=True)


    def __str__(self):
        return self.description


class ttransaction_lines(models.Model):
    transaction = models.ForeignKey(ttransactions, on_delete=models.PROTECT, db_index=True)
    sequence = models.IntegerField()
    transaction_type = models.CharField(max_length=6, choices=debit_credit)
    ledger_account = models.ForeignKey(tledger_account, on_delete=models.PROTECT, db_index=True)
    amount = models.DecimalField(max_digits=14, decimal_places=2, default=0.0)
    vat_amount = models.DecimalField(max_digits=14, decimal_places=2, default=0.0)
    vat_code = models.ForeignKey(tvat, on_delete=models.PROTECT, blank=True, null=True)
    quantity = models.IntegerField(blank=True, null=True)
    posted = models.BooleanField(default=True)
 

forms.py:

     class TransactionsForm(forms.ModelForm):
    transaction_date = forms.DateField(widget=forms.SelectDateWidget(years=year_range), initial=datetime.today)

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request')
        super(TransactionsForm, self).__init__(*args, **kwargs)

    class Meta:
        model = ttransactions
        fields = ['description',
                  'transaction_date']

class TransactionLinesForm(forms.ModelForm):
    class Meta:
        model = ttransaction_lines
        fields = ['transaction_type', 'ledger_account', 'amount']
   
class BaseTransactionLinesFormSet(BaseModelFormSet):
    def clean(self):
        super(BaseTransactionLinesFormSet, self).clean()
        # Check errors dictionary first, if there are any error, no point in validating further
        if any(self.errors):
            return
        balance = 0
        for form in self.forms:
            if form.cleaned_data['DELETE'] == True or form.cleaned_data['DELETE'] == '':
                continue
            if form.cleaned_data['transaction_type']=='Debit':
                balance = balance   form.cleaned_data['amount']
            else:
                balance = balance - form.cleaned_data['amount']

        if balance != 0:
            message = 'Transactions not balanced (excluding deleting lines)'
            raise forms.ValidationError(message)
            
TransactionLineFormset = inlineformset_factory(ttransactions,
                                               ttransaction_lines,
                                               form=TransactionLinesForm,
                                               can_order=True, can_delete=True)
 

views.py

 class JournalCreateView(LoginRequiredMixin, CreateView):
template_name = 'accounting/journal.html'
model = ttransactions
formset = TransactionLineFormset
form_class = TransactionsForm

success_url = '/accounting/transaction_list'

def get_form_kwargs(self):
    kwargs = super(JournalCreateView, self).get_form_kwargs()
    kwargs['request'] = self.request
    return kwargs

def get(self, request, *args, **kwargs):
    self.object = None
    form_class = self.get_form_class()
    form = self.get_form(form_class)

    formset = TransactionLineFormset(queryset=ttransaction_lines.objects.none())

    formset.form.base_fields['ledger_account'].queryset = 
        tledger_account.objects.filter(company=request.user.current_company)

    return self.render_to_response(
        self.get_context_data(form=form, formset=formset))

def post(self, request, *args, **kwargs):
    extra_forms = 1
    if 'additems' in request.POST and request.POST['additems'] == 'true':
        formset_dictionary_copy = self.request.POST.copy()
        formset_dictionary_copy['form-TOTAL_FORMS'] = 
            int(formset_dictionary_copy['form-TOTAL_FORMS'])   extra_forms
        formset = TransactionLineFormSet(formset_dictionary_copy)
        return self.render_to_response(
            self.get_context_data(form=form,
                                  formset=formset))

    self.object = None
    form_class = self.get_form_class()
    form = self.get_form(form_class)

    formset = TransactionLineFormset(self.request.POST)
    if (form.is_valid() and formset.is_valid()):
        return self.form_valid(form, formset)
    else:
        return self.form_invalid(form, formset)

def form_valid(self, form, formset):
    form.instance.company = self.request.user.current_company
    self.object = form.save()

    sequence = 1
    for line in formset:
        line.instance.sequence = sequence
        sequence  = 1

    formset.instance = self.object
    formset.save()
    return super().form_valid(form)

def form_invalid(self, form, formset):
    return self.render_to_response(
        self.get_context_data(form=form,
                              formset=formset))
 

Я получаю сообщение об ошибке в строке, которая копирует строку словаря. Код для добавления строки добавляется в функцию post. Я не уверен, что это правильное место для добавления этого кода. Помощь будет оценена.

Ответ №1:

Эта ошибка возникает, если в отправленной форме отсутствует ключ.

.get() Метод допускает значение по умолчанию при доступе к отсутствующему ключу в dict.

 formset_dictionary_copy['form-TOTAL_FORMS'] =  
    int(formset_dictionary_copy.get('form-TOTAL_FORMS', 1))   extra_forms
 

Похоже, что набор форм не получает все свои скрытые значения или отправляется другая форма.

Я бы рассмотрел внешнее решение или отправку одного начального набора форм со значениями кнопки добавления.

Пример интерфейса / js: https://www.brennantymrak.com/articles/django-dynamic-formsets-javascript

Комментарии:

1. Спасибо за ваш ответ. Я изменил, как рекомендовано, но теперь получаю эту ошибку: аргумент int () должен быть строкой, байтоподобным объектом или числом, а не ‘NoneType’. Пожалуйста, посмотрите еще раз.

2. form-TOTAL_FORMS значение None, которое отправляется из формы. Существует ли значение по умолчанию 1? Не изменяя слишком много кода, вы можете просто попробовать /за исключением случая None .

3. Спасибо за вашу помощь. Я переместил код additems на несколько строк ниже после formset = TransactionLineFormset(self.request.POST), и теперь он находит form-TOTAL_forms . Еще раз спасибо.