使用两个提交按钮将 django inlineformset 保存为草稿

Using two submit buttons to save django inlineformset as draft

我有一个行之有效的内联表单集。我试图添加第二个提交按钮,单击该按钮时将检查每个内联表单中的特定字段是否已填写,即仅当单击第二个提交按钮时,该字段才成为必填字段。

问题是,当我点击第二个提交按钮时,验证错误没有出现,表单似乎只是提交了。我认为这个问题在我看来,在 form_valid 之内。当 if form.is_valid() 失败时,我不确定我需要 return 什么。

我正在自学编码,因此非常感谢任何帮助或指导。

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>

当您的表单有效时,

form_valid 将被激活。在您的类视图中,django 会自动检查 checklist_form 的验证而不验证表单集。您可以在 form_validform_invalid 中添加自定义验证,但我会采用另一种方式。我会将 formset 放入您的基本表单中 (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

现在只有当 formset 也有效时,form 才有效。然后在您的 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