Использование двух кнопок отправки для сохранения django inlineformset в качестве черновика

У меня есть inlineformset, который работает. Я попытался добавить вторую кнопку отправки, которая при нажатии будет проверять, заполнено ли конкретное поле в каждой встроенной форме, т.е. это поле становится обязательным только при нажатии второй кнопки отправки.

Проблема в том, что когда я нажимаю эту вторую кнопку отправки, ошибки проверки не появляются, и форма все равно отправляется. Я думаю, что проблема, на мой взгляд, в form_valid. Я не уверен, что мне нужно возвращать, когда if form.is_valid() терпит неудачу.

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

Forms.py

class response_form_draft(forms.ModelForm):
    class Meta:
        name = response
        exclude = ['id_question','order']
    def __init__(self, *args, **kwargs):
        super(response_form_draft, self).__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.form_show_labels = False

class response_form_final(forms.ModelForm):
    class Meta:
        name = response
        exclude = ['id_question','order']
    def __init__(self, *args, **kwargs):
        super(response_form_final, self).__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.form_show_labels = False
    def clean(self):
        data = self.cleaned_data
        resp = data['response']
        if resp == "":
            raise forms.ValidationError(('must not be blank'))


checklist_formset_draft = inlineformset_factory(checklist, response,
                                        form = response_form_draft,
                                        extra=0, can_delete=False,
                                        widgets={'comments': forms.Textarea(attrs={'cols': 7, 'rows': 3,
                                                                                    'style':'resize:none'})
                                                                                    })

checklist_formset_final = inlineformset_factory(checklist, response,
                                        form = response_form_final,
                                        extra=0, can_delete=False,
                                        widgets={'comments': forms.Textarea(attrs={'cols': 7, 'rows': 3,
                                                                                    'style':'resize:none'}),
                                                'response': forms.TextInput(attrs={'required':True})
                                                })

Views.py

class ChecklistUpdateView(LoginRequiredMixin, UpdateView):
    login_url = '/user/login'
    model = checklist
    form_class = checklist_form
    success_url = reverse_lazy('checklist:checklist_list')

    def get_context_data(self, **kwargs):
        data = super(ChecklistUpdateView, self).get_context_data(**kwargs)
        if self.request.POST:
            if 'complete' in self.request.POST:
                print("complete")
                data['question'] = checklist_formset_final(self.request.POST, instance=self.object)
            else:
                print("draft")
                data['question'] = checklist_formset_draft(self.request.POST, instance=self.object)
        else:
            data['question'] = checklist_formset_draft(instance=self.object)
        return data

    def form_valid(self, form):
        context = self.get_context_data()
        question = context['question']
        with transaction.atomic():
            self.object = form.save(commit=False)
            self.object.last_edit_by = str(self.request.user)
            self.object.last_edit_date = timezone.now()
            if question.is_valid():
                question.instance = self.object
                question.save()

        return super(ChecklistUpdateView, self).form_valid(form)

HTML-шаблон

{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}


<div class="container">

<form method="post">
  {% csrf_token %}
  <h1>{{ object.id_template.title }}</h1>
  {{ form.errors }}
  {{ form.entity|as_crispy_field }}

  {{ form.date_created.as_hidden }}
  {{ form.created_by.as_hidden }}
  {{ form.id_template.as_hidden }}
  {{ form.status.as_hidden }}
<div class="card">


<table id="table_id" class="table">
  <tbody>
    {{ question.management_form }}

    {% for form in question.forms %}
        {{ formset.errors }}
        {{ form.non_field_errors }}
      {% if forloop.first %}
          <thead class="thead-dark">
          <tr>
              {% for field in form.visible_fields %}
                  <th>{{ field.label|capfirst }}</th>
              {% endfor %}
              <th></th>
          </tr>
          </thead>
      {% endif %}

      <tr class="{% cycle row1 row2 %} formset_row">

                    {% for field in form.visible_fields %}
                        <td>
                            {# Include the hidden fields in the form #}
                            {% if forloop.first %}

                                {% for hidden in form.hidden_fields %}
                                    {{ hidden }}
                                {% endfor %}
                            {% endif %}
                            {{ field.errors.as_ul }}
                            {% if field.label == "Question" %}
                              <p>{{ form.question.value }}</p>
                              {{ field.as_hidden }}
                            {% elif field.label == "Response" %}
                              {{ field.as_hidden }}
                                <button id="Yes.{{ forloop.parentloop.counter0 }}" row="{{ forloop.parentloop.counter0 }}" class="ans {{ forloop.parentloop.counter0 }} btn btn-dark right">Yes</button>
                                <button id="No.{{ forloop.parentloop.counter0 }}" row="{{ forloop.parentloop.counter0 }}" class="ans {{ forloop.parentloop.counter0 }} btn btn-dark right">No</button>
                                <button id="N/a.{{ forloop.parentloop.counter0 }}" row="{{ forloop.parentloop.counter0 }}" class="ans {{ forloop.parentloop.counter0 }} btn btn-dark right">N/a</button>


                            {% else %}
                            {{ field|as_crispy_field }}
                            {% endif %}
                        </td>
                    {% endfor %}
                    <td>  </td>
                </tr>

{% endfor %}
</tbody>
</table>
</div>
{{ form.comments|as_crispy_field }}
  {{ form.audit|as_crispy_field }}
  <div class="form-submit-row" id="submit-row">
    <input class="btn btn-dark right" name="draft "type="submit" value="Save draft">
    <input class="btn btn-dark right" name="complete" type="submit" value="Complete">
    <a href="{% url 'checklist:checklist_delete' pk=object.id %}" class="btn btn-danger right">Delete</a>
  </div>
</form>
<br><br>

</div>


person johnjo    schedule 25.09.2020    source источник


Ответы (1)


form_valid активируется, когда ваша форма действительна. В вашем классе django автоматически проверяет проверку вашего checklist_form без проверки набора форм. вы можете добавить пользовательскую проверку в свои form_valid и form_invalid, но я бы пошел другим путем. я бы поместил набор форм в вашу базовую форму (checklist_form)

что-то такое:

class checklist_form(forms.ModelForm):
    def __init__(*args, **kwargs):
       self.question= kwargs.pop("question")
       super()__.init__(*args, **kwargs)

    def clean(self):
       cleaned_data = super().clean()
       if not self.question.is_valid():
          self.add_error(None, "Have some formset errors")
       return cleaned_data

теперь форма будет действительна только в том случае, если набор форм также действителен. И тогда в вашем ChecklistUpdateView я бы поставил создание набора форм в get_form_kwargs

    def get_form_kwargs(self):
        data = super().get_form_kwargs()
        if self.request.POST:
            if 'complete' in self.request.POST:
                data['question'] = checklist_formset_final(self.request.POST, instance=self.object)
            else:
                data['question'] = checklist_formset_draft(self.request.POST, instance=self.object)
        else:
            data['question'] = checklist_formset_draft(instance=self.object)
        return data

После этого в вашем html-шаблоне вам нужно будет использовать form.question вместо question.

person Andrey Maslov    schedule 25.09.2020
comment
Спасибо. Что мне нужно добавить в get_form_kwargs, чтобы он заработал? - person johnjo; 25.09.2020
comment
просто переименуйте get_context_data в get_form_kwargs и удалите **kwargs из параметров. p.s. обновил мой ответ - person Andrey Maslov; 25.09.2020