将Django Context处理器与表单一起使用

时间:2017-11-08 19:55:58

标签: python django forms

我有多个表单可以在我的项目中随处显示,因此我读到有context_processor是最好的方法。所以,我在我的应用程序中创建了一个,它看起来像这样:

def forms_processor(request):

    name_form = NewNameForm()
    work_form = NewWorkForm()
    address_form = NewAddressForm()

    context = {'name_form': name_form,
           'work_form': work_form,
           'address_form': work_form,
           }
    return context

这很好用,我可以在我的模板中的任何地方使用{{name_form}}并呈现表单。

现在我的问题是,我在哪里验证表格?在我的views.pycontext_processors.py?现在我对name_form的看法看起来像这样:

def user_profile(request):
    if request.method == 'POST':
        name_form = NewNameForm(request.POST)

        if name_form.is_valid():
            form.save()

    else:
        ctx = {'title': 'Profile', 'active_tab': 'Profile'}

    return render (request, 'user_profile.html', ctx)

实际上这并不起作用,如果我提交的表单无效,它只会回到同一页面并且不会显示填充的表单。

如果有人可以指导我或者将我重定向到这个主题的某些文档,那就太棒了!谢谢!

2 个答案:

答案 0 :(得分:0)

The problem is that your processor instantiates the form on each render. Each time you call render, your processor is called, which instantiates a new form and displays THAT form, not the form instance that you created in the view. Therefore, the form being rendered is a blank instance but the form that contains the input and errors was destroyed by garbage collection after finishing your view.

A way I would do this, is passing the form you create in the view back to context before rendering. Pass it in to a context key such as "name_form_filled". Then if that variable is present in the context, don't render "name_form", instead render "name_form_filled".

views.py

def user_profile(request):
    ctx = {}
    if request.method == 'POST':
        name_form = NewNameForm(request.POST)

        if name_form.is_valid():
            name_form.save() # you named this named_form, not form.
            # If you want to redirect to another view when the form is saved successfuly, do it here.
        else:
            ctx["name_form_filled"] = form

    else:
        ctx.update({'title': 'Profile', 'active_tab': 'Profile'})

    return render (request, 'user_profile.html', ctx)

user_profile.html

<div id="form_container">
    {% if name_form_filled %}
    <!-- Render form that has input and errors from previous POST. -->
    {{ name_form_filled }}
    {% else %}
    <!-- render empty initial form. User has not attempted to submit yet. -->
    {{ name_form }}
    {% endif %}
</div>

===========================================================================

Another way you could do this is turn this view into a class based view and inherit a base class based view. This base class will override the get_context_data method and add your three forms. Note that you won't be using the context processor with this methodology so you could get rid of it if wanted in this case.

All views that use your form will extend the base view class. Then, after evaluating your form, if it is invalid, overwrite your name_form context key with the invalid form instance, which will be in your context.

views.py

class BaseView(View):
    def get_context_data(self, *args, **kwargs):
        context = {
            "name_form": NewNameForm(),
            "work_form": NewWorkForm(),
            "address_form": NewAddressForm()
        }
        return context


class UserProfileView(BaseView):
    def get(self, request, *args, **kwargs):
        # Do GET logic here.
        ctx = self.get_context_data(*args, **kwargs) # BaseView.get_context_data will be called here unless you override it in this class.
        ctx.update({'title': 'Profile', 'active_tab': 'Profile'})
        return render (request, 'user_profile.html', ctx)

    def post(self, request, *args, **kwargs):
        # Do POST logic here.
        ctx = self.get_context_data(*args, **kwargs) # BaseView.get_context_data will be called here unless you override it in this class.

        name_form = NewNameForm(request.POST)

        if name_form.is_valid():
            name_form.save()
        else:
            ctx["name_form"] = name_form # will replace the empty form in context with the form instance created in name_form that has input and errors.
        return render (request, 'user_profile.html', ctx)

user_profile.html

<div id="form_container">
    <!-- Will render whatever is in name_form. If this is after the
    user has submitted an invalid form, this form will be populated with input and errors because we overwrote it in the view. -->
    {{ name_form }}
</div>

===========================================================================

I personally think that the first solution is the best but when you start getting more complex, you should probably switch over to the second solution as class based views make complex views way easier.

答案 1 :(得分:0)

直接回答:您使用views.py方法在is_valid()中验证表单。如果表单无效,您需要使用绑定表单填充上下文:

def user_profile(request):
    ctx = {'title': 'Profile', 'active_tab': 'Profile'}

    if request.method == 'POST':
        name_form = NewNameForm(request.POST)
        if name_form.is_valid():
            form.save()
            return redirect(YOUR_REDIRECT_URL) # Always redirect after successful POST
        ctx['form'] = form  # if form is invalid return it with context

    return render (request, 'user_profile.html', ctx)

documentation中阅读更多内容。