Django - 更新内联表单集不更新

问题描述

我正在尝试创建一个更新视图,其中包含一些内联表单集,但由于某种原因,我看到“id:此字段是必需的”。 & 当我单击更新按钮时,没有设置任何内联表单集值。字段本身实际上在模板中有值,所以我不确定为什么在尝试保存时内联表单集会是空的。

Models.py

class ProjectUpdateForm(forms.ModelForm):
class Meta:
    model = Project
    fields = ('name','details')

Views.py

class ProjectUpdateView(LoginrequiredMixin,UpdateView):
model = Project
template_name = 'home/project/project_update.html'
context_object_name = "project"
form_class = ProjectUpdateForm

def get_context_data(self,**kwargs):
    ReviewChildFormset = inlineformset_factory(
        Project,AdoptedBudgetReview,fields=('project','review'),can_delete=False,extra=0
    )

    itemNames = [{'item': item} for item in ['Concept & Feasibility','Planning & Design','Procurement','Delivery & Construction','Finalisation']]
    EstimatedBudgetChildFormset = inlineformset_factory(
        Project,EstimatedBudget,'item','cost','time'),formset=EstimatedInlineFormset,extra=0,widgets={'item': forms.Select(attrs={'disabled': True})},)

    FutureExpenditureFormset = inlineformset_factory(
        Project,FutureExpenditure,'byear','internalFunding','externalFundingSource','externalFunding'),)

    PrevIoUsExpenditureFormset = inlineformset_factory(
        Project,PrevIoUsExpenditure,)

    initial = [{'priority': priority} for priority in Priority.objects.all()]
    PriorityChildFormset = inlineformset_factory(
        Project,ProjectPriority,'priority','score'),widgets={'priority': forms.Select(attrs={'disabled': True})},)

    context = super().get_context_data(**kwargs)
    if self.request.POST:
        context['adoptedreviews'] = ReviewChildFormset(self.request.POST,instance=self.object)
        context['estimatedbudget'] = EstimatedBudgetChildFormset(self.request.POST,initial=itemNames,instance=self.object)
        context['futureexpenditure'] = FutureExpenditureFormset(self.request.POST,instance=self.object)
        context['prevIoUsexpenditure'] = PrevIoUsExpenditureFormset(self.request.POST,instance=self.object)
        context['priorities'] = PriorityChildFormset(self.request.POST,initial=initial,instance=self.object)
    else:
        context['adoptedreviews'] = ReviewChildFormset(instance=self.object)
        context['estimatedbudget'] = EstimatedBudgetChildFormset(initial=itemNames,instance=self.object)
        context['futureexpenditure'] = FutureExpenditureFormset(instance=self.object)
        context['prevIoUsexpenditure'] = PrevIoUsExpenditureFormset(instance=self.object)
        context['priorities'] = PriorityChildFormset(initial=initial,instance=self.object)
        
    return context

def form_valid(self,form):
    context = self.get_context_data()
    adoptedreview = context["adoptedreviews"]
    estimatedbudget = context["estimatedbudget"]
    prioritycriteria = context["priorities"]
    futureexpenditure = context["futureexpenditure"]
    prevIoUsexpenditure = context["prevIoUsexpenditure"]
    form.instance.creator = self.request.user
    if adoptedreview.is_valid() and estimatedbudget.is_valid() and prevIoUsexpenditure.is_valid() and futureexpenditure.is_valid and prioritycriteria.is_valid():

        self.object = form.save()
        adoptedreview.instance = self.object
        estimatedbudget.instance = self.object
        futureexpenditure.instance = self.object
        prevIoUsexpenditure.instance = self.object
        prioritycriteria.instance = self.object

        adoptedreview.save()
        estimatedbudget.save()
        prevIoUsexpenditure.save()
        futureexpenditure.save()
        prioritycriteria.save()
    else:
        return self.form_invalid(form)
    return super(ProjectCreateview,self).form_valid(form)

模板

 <form method="POST">
        {% csrf_token %}
        <fieldset class="form-group">
            <legend class="border-bottom mb-4">Update Project</legend>
            {{ form|crispy }}
        </fieldset>

        <h2>Adopted Budget Review</h2>
        <!-- {{ adoptedreviews|crispy }} -->
        <table class="table table-light">
          <thead>
            <tr>
              <th scope="col"></th>
              <th scope="col">BR1</th>
              <th scope="col">BR2</th>
              <th scope="col">BR3</th>
              <th scope="col">BR4</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <th scope="row">Amount</th>
              {{ adoptedreviews.management_form }}
              {% for adrform in adoptedreviews.forms %}
                 {% for field in adrform.visible_fields %}
                 <td>
                   {{ field.errors.as_ul }}
                   {{ field }}
                 </td>
                 {% endfor %}
              {% endfor %}
            </tr>
          </tbody>
        </table>

        <h2>Estimated Budget - Breakdown Yr1</h2>
        <!-- {{ estimatedbudget|crispy }} -->
        <table class="table table-light">
          {{ estimatedbudget.management_form }}
          <thead>
            <tr>
              <th scope="col">Item - Description</th>
              <th scope="col">Anticipated % cost</th>
              <th scope="col">Anticipated time - weeks</th>
            </tr>
          </thead>
          {% for form in estimatedbudget.forms %}
            <tr>
              <td>
                {{ form.errors }}
                <h4>{{ form.item.value }}</h4>
                {{ form.item.as_hidden }}
              </td>
              <td>
                {{ form.cost }}
              </td>
              <td>
                {{ form.time }}
              </td>
            </tr>
            {% endfor %}
         </table>

        <h2>Future Project Expenditure</h2>
        <!-- {{ futureexpenditure|crispy }} -->
        <table class="table table-light">
          {{ futureexpenditure.management_form }}
          {% for form in futureexpenditure.forms %}
              {% if forloop.first %}
                  <thead>
                  <tr>
                      {% for field in form.visible_fields %}
                          <th scope="col">{{ field.label|capfirst }}</th>
                      {% endfor %}
                  </tr>
                  </thead>

              {% endif %}
                <tbody>
                  <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 }}
                            {{ field }}
                        </td>
                    {% endfor %}
                  </tr>
                </tbody>
            {% endfor %}
         </table>

         <h2>PrevIoUs Project Expenditure</h2>
         <table class="table table-light">
           <!-- {{ prevIoUsexpenditure|crispy }} -->
          {{ prevIoUsexpenditure.management_form }}
          {% for form in prevIoUsexpenditure.forms %}
              {% if forloop.first %}
                  <thead>
                  <tr>
                      {% for field in form.visible_fields %}
                          <th scope="col">{{ field.label|capfirst }}</th>
                      {% endfor %}
                  </tr>
                  </thead>

              {% endif %}
                <tbody>
                  <tr class="{% cycle row1 row2 %} prevIoUs_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 }}
                            {{ field }}
                        </td>
                    {% endfor %}
                  </tr>
                </tbody>
            {% endfor %}
         </table>

        <h2>Priority Criteria</h2>
        <!-- {{ priorities|crispy }} -->
        <table class="table table-light">
          <tbody>
            {{ priorities.management_form }}
            {% for priority in priorities.forms %}
            <tr>
              <td>
                {{ priority.priority.errors.as_ul }}
                {% for value,name in priority.priority.field.choices %}
                  {% if value == priority.priority.value %}
                    <h4>{{ name }}</h4>
                  {% endif %}
                {% endfor %}
                {{ priority.priority.as_hidden }}
              </td>
              <td>
                {{ priority.score.errors.as_ul }}
                {{ priority.score }}
              </td>
            </tr>
            {% endfor %}
          </tbody>
        </table>

        <div class="form-group">
            <button class="btn btn-outline-info" type="submit">Update Project</button>
        </div>
    </form>

解决方法

无论何时手动渲染表单,都应该记住总是渲染它的隐藏字段,对于表单集/内联表单集更是如此,因为它会自动向表单添加一些隐藏字段.由于表单集正在更新多个实例,当然需要在表单集中存在一些东西来指示哪个实例是哪个,这是由这里的隐藏字段完成的。

因此您需要呈现表单集表单的隐藏字段:

<form method="POST">
        {% csrf_token %}
        <fieldset class="form-group">
            <legend class="border-bottom mb-4">Update Project</legend>
            {{ form|crispy }}
        </fieldset>

        <h2>Adopted Budget Review</h2>
        <!-- {{ adoptedreviews|crispy }} -->
        <table class="table table-light">
          <thead>
            <tr>
              <th scope="col"></th>
              <th scope="col">BR1</th>
              <th scope="col">BR2</th>
              <th scope="col">BR3</th>
              <th scope="col">BR4</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <th scope="row">Amount</th>
              {{ adoptedreviews.management_form }}
              {% for adrform in adoptedreviews.forms %}
                 {% for hidden in adrform.hidden_fields %}
                     {{ hidden }}
                 {% endfor %}
                 {% for field in adrform.visible_fields %}
                 <td>
                   {{ field.errors.as_ul }}
                   {{ field }}
                 </td>
                 {% endfor %}
              {% endfor %}
            </tr>
          </tbody>
        </table>

        <h2>Estimated Budget - Breakdown Yr1</h2>
        <!-- {{ estimatedbudget|crispy }} -->
        <table class="table table-light">
          {{ estimatedbudget.management_form }}
          <thead>
            <tr>
              <th scope="col">Item - Description</th>
              <th scope="col">Anticipated % cost</th>
              <th scope="col">Anticipated time - weeks</th>
            </tr>
          </thead>
          {% for form in estimatedbudget.forms %}
            {% for hidden in form.hidden_fields %}
                {{ hidden }}
            {% endfor %}
            <tr>
              <td>
                {{ form.errors }}
                <h4>{{ form.item.value }}</h4>
                {{ form.item.as_hidden }}
              </td>
              <td>
                {{ form.cost }}
              </td>
              <td>
                {{ form.time }}
              </td>
            </tr>
            {% endfor %}
         </table>

        <h2>Future Project Expenditure</h2>
        <!-- {{ futureexpenditure|crispy }} -->
        <table class="table table-light">
          {{ futureexpenditure.management_form }}
          {% for form in futureexpenditure.forms %}
              {% if forloop.first %}
                  <thead>
                  <tr>
                      {% for field in form.visible_fields %}
                          <th scope="col">{{ field.label|capfirst }}</th>
                      {% endfor %}
                  </tr>
                  </thead>

              {% endif %}
                <tbody>
                  <tr class="{% cycle row1 row2 %} formset_row">
                    {% for hidden in form.hidden_fields %}
                        {{ hidden }}
                    {% endfor %}
                    {% 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 }}
                            {{ field }}
                        </td>
                    {% endfor %}
                  </tr>
                </tbody>
            {% endfor %}
         </table>

         <h2>Previous Project Expenditure</h2>
         <table class="table table-light">
           <!-- {{ previousexpenditure|crispy }} -->
          {{ previousexpenditure.management_form }}
          {% for form in previousexpenditure.forms %}
              {% if forloop.first %}
                  <thead>
                  <tr>
                      {% for field in form.visible_fields %}
                          <th scope="col">{{ field.label|capfirst }}</th>
                      {% endfor %}
                  </tr>
                  </thead>

              {% endif %}
                <tbody>
                  <tr class="{% cycle row1 row2 %} previous_formset_row">
                    {% for hidden in form.hidden_fields %}
                        {{ hidden }}
                    {% endfor %}
                    {% 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 }}
                            {{ field }}
                        </td>
                    {% endfor %}
                  </tr>
                </tbody>
            {% endfor %}
         </table>

        <h2>Priority Criteria</h2>
        <!-- {{ priorities|crispy }} -->
        <table class="table table-light">
          <tbody>
            {{ priorities.management_form }}
            {% for priority in priorities.forms %}
            {% for hidden in priority.hidden_fields %}
                {{ hidden }}
            {% endfor %}
            <tr>
              <td>
                {{ priority.priority.errors.as_ul }}
                {% for value,name in priority.priority.field.choices %}
                  {% if value == priority.priority.value %}
                    <h4>{{ name }}</h4>
                  {% endif %}
                {% endfor %}
                {{ priority.priority.as_hidden }}
              </td>
              <td>
                {{ priority.score.errors.as_ul }}
                {{ priority.score }}
              </td>
            </tr>
            {% endfor %}
          </tbody>
        </table>

        <div class="form-group">
            <button class="btn btn-outline-info" type="submit">Update Project</button>
        </div>
    </form>

此外,由于您有多个表单集,您应该为它们提供前缀以提供名称冲突(请参阅 Using more than one formset in a view):

if self.request.POST:
    context['adoptedreviews'] = ReviewChildFormset(self.request.POST,instance=self.object,prefix='adoptedreviews_form')
    context['estimatedbudget'] = EstimatedBudgetChildFormset(self.request.POST,initial=itemNames,prefix='estimatedbudget_form')
    context['futureexpenditure'] = FutureExpenditureFormset(self.request.POST,prefix='futureexpenditure_form')
    context['previousexpenditure'] = PreviousExpenditureFormset(self.request.POST,prefix='previousexpenditure_form')
    context['priorities'] = PriorityChildFormset(self.request.POST,initial=initial,prefix='priorities_form')
else:
    context['adoptedreviews'] = ReviewChildFormset(instance=self.object,prefix='adoptedreviews_form')
    context['estimatedbudget'] = EstimatedBudgetChildFormset(initial=itemNames,prefix='estimatedbudget_form')
    context['futureexpenditure'] = FutureExpenditureFormset(instance=self.object,prefix='futureexpenditure_form')
    context['previousexpenditure'] = PreviousExpenditureFormset(instance=self.object,prefix='previousexpenditure_form')
    context['priorities'] = PriorityChildFormset(initial=initial,prefix='priorities_form')

注意:您似乎有奇怪的 HTML(表格中有多个 tbody 元素??),而且您可能想要重新考虑这么多 页面中的表单集,这对于用户体验而言可能被认为是不利的(这 可能会真的让用户感到困惑,更不用说开发人员了 也)。理想情况下,一页应该为用户服务一个目的。