Django上下文处理器和csrf_token

5
我有一个登录表单,希望在所有视图中都能使用,因此创建了一个上下文处理器以将该表单添加到所有加载的上下文中。
问题是,表单模板上的{% csrf_token %}不会呈现CSRF令牌值的隐藏输入标签。
以下是settings.py中的上下文处理器顺序:
TEMPLATE_CONTEXT_PROCESSORS = (
    'django.contrib.auth.context_processors.auth',
    'django.core.context_processors.debug',
    'django.core.context_processors.i18n',
    'django.core.context_processors.media',
    'django.core.context_processors.static',
    'django.core.context_processors.tz',
    'django.contrib.messages.context_processors.messages',
    'django.core.context_processors.request',
    'django.core.context_processors.csrf',
    'absolute.context_processors.absolute',
    'myproject.app.context_processors.base',
)

然后处理器本身位于app/context_processors.py

from django.contrib.auth.forms import AuthenticationForm

def base(request):
    context = dict()
    if not request.user.is_authenticated():
        context['login_form'] = AuthenticationForm()
    return context

表单模板:
{% load i18n %}

<form method="post" action="{% url "django.contrib.auth.views.login" %}">

    {% csrf_token %}
    <input type="hidden" name="next" value="{% if request.GET.next %}{{ request.GET.next }}{% else %}{{ request.get_full_path }}{% endif %}" />

    {{ login_form.as_p }}

    <input type="submit" class="button success expand" value="{% trans 'Login' %}" />

</form>

这个表单的HTML输出为:

<form action="/accounts/login/" method="post">


    <input type="hidden" value="/" name="next">

    <p><label for="id_username">Usuário:</label> <input type="text" name="username" maxlength="254" id="id_username"></p>
    <p><label for="id_password">Senha:</label> <input type="password" name="password" id="id_password"></p>

    <input type="submit" value="Login" class="button success expand">

</form>

当我提交时出现的错误:

CSRF verification failed. Request aborted.

然而,由于我只使用基于类的视图,如果我添加一个csrf_protect装饰器,表单将会正常工作,但是这样一来我必须在所有的视图中声明dispatch方法:

from django.views.decorators.csrf import csrf_protect

class HomeView(TemplateView):
    template_name = 'home.html'

    @method_decorator(csrf_protect)
    def dispatch(self, *args, **kwargs):
        return super(HomeView, self).dispatch(*args, **kwargs)

问题状态

我放弃了在所有视图中放置AuthenticationForm,而是创建了一个登录表单页面。无论如何,如果有人能帮助我找到解决这个问题的方法,那将是非常棒的。


一个猜测:从 'myproject.app.context_processors.base' 中删除 myproject. - Steinar Lima
当模板被渲染时,输出的HTML是什么?无论您是否使用上下文处理器,csrf_token是否显示都不应受到影响。您是否完全使用了JavaScript? - Alasdair
1
@SteinarLima,从上下文处理器导入字符串中删除myproject将会导致导入错误:No module named app.context_processors - vmassuchetto
你发布的表单模板和home.html是一样的吗?如果不是,那么表单模板是如何被包含在home.html中的? - Collin Anderson
你使用的是哪个版本的Django?在Django 1.1中,csrf_token标签没有任何作用。 - Collin Anderson
显示剩余5条评论
2个回答

3

我花了几个小时来解决类似于你在这里描述的问题。在调试模式下,{% csrf_token %} 没有渲染任何内容,我看到以下警告信息:

defaulttags.py:66: UserWarning: 模板中使用了 {% csrf_token %} 但是上下文没有提供值。通常是由于没有使用 RequestContext 导致的。

我正在使用从 TemplateView 继承而来的简单视图:

class MenuIndexView(TemplateView):
    template_name = 'menu/index.html'

    def get_context_data(self, **kwargs):
        kwargs = super().get_context_data(**kwargs)

        session = self.request.session
        kwargs['zip_code'] = session.get('zip_code')
        kwargs['calendar'] = helpers.get_menu_calendar(date.today() + timedelta(days=1), timedelta(days=14))
        kwargs['forms'] = {'zip_code': forms.ZipCodeForm({'zip_code': session.get('zip_code')})}

        return kwargs

在 Django 中研究了一下后,我发现在生成标签 (CsrfTokeNode 文件位于 Django 的 defaulttags.py) 的时候几乎没有可用的上下文:

class CsrfTokenNode(Node):
    def render(self, context):
        csrf_token = context.get('csrf_token', None)
        if csrf_token:
            if csrf_token == 'NOTPROVIDED':
                return format_html("")
            else:
                return format_html("<input type='hidden' name='csrfmiddlewaretoken' value='{}' />", csrf_token)
        else:
            # It's very probable that the token is missing because of
            # misconfiguration, so we raise a warning
            if settings.DEBUG:
                warnings.warn(
                    "A {% csrf_token %} was used in a template, but the context "
                    "did not provide the value.  This is usually caused by not "
                    "using RequestContext."
                )
            return ''

在这段代码中,我只看到了上下文中的一个项目,其中包含一个 zip_code 键。
我打开了主模板文件,并意识到我犯了一个新手错误 - 这是我的主模板 menu/index.html:
{% extends 'base.html' %}

{% block content %}
    <div class="menu-order-meta zip_calendar">
        <div class="ink-grid align-center">
            <div class="column-group gutters half-vertical-padding medium">
                {% include 'partials/menu/zip-code.html' with zip_code=zip_code only %}
            </div>
        </div>
    </div>
{% endblock %}

我通过部分模板包含了表单,并且在该部分模板中明确限制了可用的上下文——请注意 with zip_code=zip_code only 声明——(这样做,隐式地使 csrf_token 上下文处理程序不可用)。


0

你可能在 MIDDLEWARE_CLASSES 中缺少 django.middleware.csrf.CsrfViewMiddleware ?

MIDDLEWARE_CLASSES = (
    ....
    'django.middleware.csrf.CsrfViewMiddleware',
    ....
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接