获取错误 403(CSRF 令牌丢失或不正确)

Getting error 403 (CSRF token missing or incorrect)

我需要一些电子邮件表格,我正在尝试这样做:

views.py

def send_email(request):
    if request.method != 'POST':
        form = EmailForm()
        return render_to_response('mail_form.html', {'email_form': form})

    form = EmailForm(request.POST, request.FILES)   
    if form.is_valid():
        subject = form.cleaned_data['subject']
        message = form.cleaned_data['message']
        email = form.cleaned_data['email']
        attach = request.FILES['attach']
        try:
            mail = EmailMessage(subject, message, settings.EMAIL_HOST_USER, [email])
            mail.attach(attach.name, attach.read(), attach.content_type)
            mail.send()
            return render(request, 'mail_form.html', {'message': 'Sent email to %s'%email})
        except:
            return render(request, 'mail_form.html', {'message': 'Either the attachment is too  big or corrupt'})
        return render(request, 'mail_form.html', {'message': 'Unable to send email. Please try again later'})

forms.py

class EmailForm(forms.Form):
    email = forms.EmailField()
    subject = forms.CharField(max_length=100)
    attach = forms.Field(widget=forms.FileInput)
    message = forms.CharField(widget = forms.Textarea)

mail_form.html

...
{{message}}
<form method="post" action="">
    {% csrf_token %}
    {{ email_form.as_p }}
    <input type ="submit"  name = "send" value = "Send"/>
</form>
...

但我经常收到错误 403。我尝试了来自网络的不同解决方案,但没有任何帮助。我做错了什么?我知道 views.py 中的 csrf 有问题,但不明白具体问题出在哪里。

您的问题是render_to_reponse。它没有您可以添加的上下文实例,但是 render 会为您处理这个,所以为什么不直接添加它呢。您也可以重组您的视图,使其更清晰一些。

举个例子。

def send_email(request):

    if request.method == 'POST':
        form = EmailForm(request.POST, request.FILES)   
        if form.is_valid():
            subject = form.cleaned_data['subject']
            message = form.cleaned_data['message']
            email = form.cleaned_data['email']
            attach = request.FILES['attach']
            try:
                mail = EmailMessage(subject, message, settings.EMAIL_HOST_USER, [email])
                mail.attach(attach.name, attach.read(), attach.content_type)
                mail.send()
                messages.succes(request, 'Sent an email to %s' % email)
            except:
                messages.error(request, 'Either the attachment is too  big or corrupt')
    else:
        form = EmailForm()
        messages.info(request, "Send an email!")
    return render(request, 'mail_form.html', {'email_form': form})

然后您可以在模板中使用 {% if messages %} 向用户显示您的消息/迭代它们并显示。

messages 这里来自 django.contrib 所以你需要做 from django.contrib import messages

  1. 使用 django.shortcuts
  2. 中的 render_to_response
  3. render_to_response中使用context_instance = RequestContext(request)

这必须解决您的 csrf 令牌问题或阅读 https://docs.djangoproject.com/ja/1.9/ref/csrf/

只需像这样修改您的view.py

from django.shortcuts import render
from django.template import RequestContext
def send_email(request):
if request.method != 'POST':
    form = forms.EmailForm()
    return render_to_response('mail_form.html', {'email_form': form}, context_instance=RequestContext(request))

......
......

What version of django are you using?

嗯,显然您在部分代码中使用了 render()。问题出在您的 GET 代码中——您使用的是 render_to_response():

if request.method != 'POST':
    form = EmailForm()
    return render_to_response('mail_form.html', {'email_form': form})

改为使用 render():

    return render(request, 'mail_form.html', {'email_form': form} )

example in the Django docs

您需要这样做的原因是因为使用 csrf 令牌您需要:

  1. 在您的表单中插入 csrf 令牌。

  2. 将 csrf 令牌作为 cookie 包含在 request/response 的 headers 中。

render() 完成了#2,但是 render_to_response() 没有——除非你特别告诉它,而你没有。在任何情况下,django 1.9 docs 状态:

render_to_response()

This function preceded the introduction of render() and works similarly except that it doesn’t make the request available in the response. It’s not recommended and is likely to be deprecated in the future.