Django在UpdateView

时间:2016-01-25 12:03:01

标签: python django

我有一个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)获取与所请求的注意相关的所有对话记录:     dialogs = Note.objects.filter(pk = self.kwargs ['pk'])

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

3)如果记录为dialogs但未通过POST提交,则该对话框被认为是由用户删除的。因此,预成型删除操作。

我相信我可以实施这些步骤。但是因为我对Django的形式不是很熟悉。我想知道是否有任何内置类或方法可用于自动执行这些步骤。做我刚才描述的 Django方式是什么?

2 个答案:

答案 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正常工作,您将需要以下隐藏的输入:

  1. 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">
    
  2. 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 -->
    
  3. [当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"> 
    
  4. 在我的模板中,我有前两个:

    <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方法。