我有一个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
请求时呈现现有的GET
和note/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
)
最后,现在一切正常