我有一个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)获取与所请求的注意相关的所有对话记录: dialogs = Note.objects.filter(pk = self.kwargs ['pk'])
2)循环遍历self.request.POST并查看提交的数据中包含的表单集是否也存在于上面创建的dialogs
中。
3)如果记录为dialogs
但未通过POST提交,则该对话框被认为是由用户删除的。因此,预成型删除操作。
我相信我可以实施这些步骤。但是因为我对Django的形式不是很熟悉。我想知道是否有任何内置类或方法可用于自动执行这些步骤。做我刚才描述的 Django方式是什么?
答案 0 :(得分:2)
好的,我弄清楚问题是什么。我遇到的问题是由于使用了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.因此,我必须专门重新定义Layout
下我的textarea字段的样子。我在使用Layout
时没有意识到的是,你未在其中定义的任何内容都不会在HTML中呈现。
从代码的外观来看,我没有错过Dialog
模型中定义的任何字段。但是,我没有意识到,InlineFormSet附带的隐藏字段不会在HTML中呈现,除非我在Layout对象和模板中明确定义它们。获得formset&amp; 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时删除隐藏字段](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
)
最后,现在一切正常
答案 1 :(得分:1)
Django的方法是检查是否有人为您处理了这个库:-)。
所以,看看优秀的django-extra-views,它是InlineFormSetView。我已经使用了很多而且效果很好。在您的情况下,您的视图会变成这样:
from extra_views import InlineFormSetView
class NoteUpdateView(InlineFormSetView):
model = Note
inline_model = Dialog
form_class = NoteForm
extra = 0
def get_context_data(self, **kwargs):
context = super(NoteUpdateView ,self).get_context_data(**kwargs)
context['dialog'] = context['formset']
return context
如果您更新模板以将formset引用为“formset”,则可以跳过.get_context_data
方法。