我有一个UpdateView,其中包含一个表单和一个与表单模型相关的InlineFormetSet(我简化了下面的代码):

#models.py
class Note(Model):
    content = models.TextField()

class Dialog(Model):
    message = models.TextField()
    note = modes.ForeignKey(Note)


#views.py
class NoteUpdateView(UpdateView):
    model = Note
    form_class = NoteForm

    def get_context_data(self, **kwargs):
        context = super(NoteUpdateView ,self).get_context_data(**kwargs)
        self.object = self.get_object()
        dialogFormset = inlineformset_factory(Note,
                                              Dialog,
                                              fields='__all__',
                                              extra=0)
        dialog = dialogFormset(instance=self.object)
        context['dialog'] = dialog
        return context

    def post(self, request, *args, **kwargs):
        form = self.get_form(self.get_form_class())
        dialog_form = DialogFormset(self.request.POST, instance=Note.objects.get(id=self.kwargs['pk']))

        if (form.is_valid() and dialog_form.is_valid()):
            return self.form_valid(form, result_form, dialog_form)
        else:
            return self.form_invalid(form, result_form, dialog_form)

    def form_valid(self, form, result_form, dialog_form):
        self.object, created = Note.objects.update_or_create(pk=self.kwargs['pk'], defaults=form.cleaned_data)
        dialog_form.save()
        return HttpResponseRedirect(self.get_success_url())


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


NoteUpdateView的目的是在向Note发出Dialog请求时呈现现有的GETnote/11。用户可以删除现有的Dialog,上面的代码未处理。

要处理删除,我可以在POST上执行以下操作:

1)提取与请求的注有关的所有对话框记录:
    对话框= Note.objects.filter(pk = self.kwargs ['pk'])

2)遍历self.request.POST并查看提交的数据中包含的表单集是否也存在于上面创建的dialogs中。

3)如果记录是dialogs,但未通过POST提交,则该对话框被用户删除。因此,执行瓶坯删除操作。

我确信我可以执行这些步骤。但是由于我对Django的格式集不是很熟悉。我想知道是否应该使用任何内置的类或方法来自动执行这些步骤。 Django的工作方式是什么?

最佳答案

好的,我找出了问题所在。我遇到的问题是由于使用django-crispy-forms。让我解释一下发生了什么:

当Django渲染InlineFormSet时,它的can_delete attribute设置为True automatically。当此属性设置为True时,会将隐藏的输入字段插入呈现的HTML中:

<input type="hidden" name="dialog_set-0-DELETE" id="id_dialog_set-0-DELETE">


我使用django-crispy-forms渲染表单,以便使用bootstrap3设置样式。使用crispy-forms渲染inlineformset时,需要定义FormHelper

这是因为当页面上有多个inlineformset表单时,您只希望一个<form>标签围绕它们,而不是为每个inlineformset表单赋予自己的<form>标签。为此,我必须这样定义FormHelper

#models.py
class Dialog(Model):
    info1 = models.TextField()
    info2 = models.TextField()

#forms.py
class DialogFormSetHelper(FormHelper):
 def __init__(self, *args, **kwargs):
    super(DialogFormSetHelper, self).__init__(*args, **kwargs)
    self.form_tag = False    # This line removes the '<form>' tag
    self.disable_csrf = True # No need to insert the CSRF string with each inlineform
    self.layout = Layout(
        Field('info1', rows='3'), # make sure the name of the field matches the names defined in the corresponding model
        Field('info2', rows='3')
    )


我需要django-crispy-forms将textarea标记的行号设置为3。因此,我必须专门重新定义我的textarea字段在Layout下的外观。使用Layout时,我没有意识到的是,您在其中定义的所有内容都不会在HTML中呈现。

从代码的外观来看,我没有错过Dialog模型中定义的任何字段。但是,我没有意识到的是,除非我在Layout对象和模板中明确定义它们,否则InlineFormSet附带的隐藏字段不会在HTML中呈现。为了使formset和inlineformset正常工作,您需要以下隐藏的输入:


formset.manageform。它们在HTML中看起来像这样:

<input id="id_dialog_set-TOTAL_FORMS" name="dialog_set-TOTAL_FORMS" type="hidden" value="1">
<input id="id_dialog_set-INITIAL_FORMS" name="dialog_set-INITIAL_FORMS" type="hidden" value="1">
<input id="id_dialog_set-MIN_NUM_FORMS" name="dialog_set-MIN_NUM_FORMS" type="hidden" value="0">
<input id="id_dialog_set-MAX_NUM_FORMS" name="dialog_set-MAX_NUM_FORMS" type="hidden" value="1000">

The primary key that is associated with each inlineformset form, and a foreign key that the inlineformset refers to。它们在HTML中看起来像这样:

<input id="id_dialog_set-0-note" name="dialog_set-0-note" type="hidden" value="11">  <!-- This line identifies the foreign key`s id -->
<input id="id_dialog_set-0-id" name="dialog_set-0-id" type="hidden" value="4"> <!-- This line identifies the inlineformset form`s id -->

[将can_delete设置为True时的DELETE隐藏字段](https://docs.djangoproject.com/en/1.9/topics/forms/formsets/#can-delete)。在HTML中看起来像这样:

<input type="hidden" name="dialog_set-0-DELETE" id="id_dialog_set-0-DELETE">



在我的模板中,我涵盖了前两个:

<form method="post" action="{{ action }}" enctype="multipart/form-data" id="note_form">
    {% crispy form %}

    {# the management_form is covered here #}
    {{ dialog.management_form }}

    {% for form in dialog %}
        <div class="formset-container">
            <div class="dialog-title">
                {% crispy form dialogHelper %}
            </div>

            {# the hidden fields are covered here #}
            {% for hidden in form.hidden_fields %}
                {{ hidden }}
            {% endfor %}

         </div>
    {% endfor %}
</form>


我没有的是DELETE隐藏的输入。要将其添加到HTML,我必须在Layout中以这种方式添加它:

#forms.py
class DialogFormSetHelper(FormHelper):
 def __init__(self, *args, **kwargs):
    super(DialogFormSetHelper, self).__init__(*args, **kwargs)
    self.form_tag = False
    self.disable_csrf = True
    self.layout = Layout(
        Field('info1', rows='3'),
        Field('info2', rows='3'),
        Field('DELETE')  # <- ADD THIS LINE
    )


最后,现在一切正常

10-08 04:07