【问题标题】:Using two submit buttons to save django inlineformset as draft使用两个提交按钮将 django inlineformset 保存为草稿
【发布时间】:2020-09-25 10:49:31
【问题描述】:

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

问题是,当我单击第二个提交按钮时,不会出现验证错误,而且表单似乎还是提交了。我认为这个问题在我看来,在 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>

【问题讨论】:

    标签: django forms inline-formset


    【解决方案1】:

    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
    

    现在表单只有在表单集也有效时才有效。然后在您的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

    【讨论】:

    • 谢谢。我需要在 get_form_kwargs 中添加什么才能使其工作?
    • 只需将get_context_data 重命名为get_form_kwargs 并从参数中删除**kwargs。 p.s.更新了我的答案
    猜你喜欢
    • 1970-01-01
    • 2014-05-28
    • 1970-01-01
    • 1970-01-01
    • 2013-08-31
    • 2020-08-28
    • 1970-01-01
    • 1970-01-01
    • 2017-05-03
    相关资源
    最近更新 更多