How to Redirect with Context in Django

  |   Source

The following question on StackOverflow :

I have a view that validates and saves a form. After the form is saved, I'd like redirect back to a list_object view with a success message "form for customer xyz was successfully updated..."

HttpResponseRedirect doesn't seem like it would work, because it only has an argument for the url, no way to pass dictionary with it.

I've tried modifying my wrapper for object_list to take a dict as a parameter that has the necessary context. I the return a call to this wrapper from inside the view that saves the form. However, when the page is rendered, the url is '/customer_form/' rather than '/list_customers/'. I tried modifying the request object, before passing it to the object_list wrapper, but that did not work.

在做非Ajax表单验证的时候,后端的验证经常是必不可少的,比如最常见的登录验证。如果验证出错,Server端需要根据不同的验证结果和错误信息,构造一个RequestContext对象,在render模板的时候传递给前端(一般是提交表单的同页面),构成对用户的提示。

该题主的问题应该是初学Django Template时比较容易遇到的一个思考误区。查阅Django 1.8.2的文档可以发现:

class HttpResponseRedirect

The first argument to the constructor is required – the path to redirect to. This can be a fully qualified URL (e.g. 'http://www.yahoo.com/search/') or an absolute path with no domain (e.g. '/search/'). See HttpResponse for other optional constructor arguments. Note that this returns an HTTP status code 302.

url

This read-only attribute represents the URL the response will redirect to (equivalent to the Location response header).

主要令人困惑的地方在于,HttpResponseRedirectHttpResponse的Subclass,然而它的构造函数参数仅只有url一个,且超过一个时不报错。暂时重定向请求(302)当然是无法传递Context变量的,妥当的做法应该是在做后端验证的url上重新构造HttpResponse对象,渲染原模板。

当然,总有些时候,后端程序员会希望无论怎么提交验证,url始终保持在原始的表单递交页面不变(即便验证的url和渲染表单的url是不同的,这也恰是通常的场景)。这样,/login/的表单提交到/user_auth/又能重定向回/login/,而不是停留在/user_auth/渲染带错误信息的表单页面,能够保持更好的一致性。

正好我在上一篇日志中也提到过这种需求,并用django.form.util.ErrorList实现;而这次基于更普遍的应用场景——例如,没有使用Django Form,或根本不是表单提交,自然也没有每个表单域自带的ErrorList Field,却依然需要传递提示消息——给出一种使用message库的新解决方案。

实现

StackOverflow上被接受的答案已经过时,使用了在Django 1.2之前存在的request.user.message_set;而高票答案的做法是可行的。

Python代码如下:

from django.contrib import messages

# messages.add_message(request, level, message, extra_tags='', fail_silently=False)
messages.add_message(request, messages.INFO, "Your Message")

前端的模板中对应的显示消息的部分:

{% if messages %}
    <ul class="messages">
    {% for message in messages %}
        <li>{{ message }}</li>
        {% endfor %}
    </ul>
{% endif %}

参考

Comments powered by Disqus
Share