Hahore-kun
Hahore-kun

Reputation: 17

Getting error 403 (CSRF token missing or incorrect)

I'm need some email form and I'm trying this:

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>
...

But constantly I get an error 403. I tried different solutions from the web, but nothing helped. What I'm doing wrong? I'm understand that something wrong with csrf in views.py, but don't understand where is the problem concretely.

Upvotes: 0

Views: 1663

Answers (4)

7stud
7stud

Reputation: 48649

What version of django are you using?

Well, obviously you are using render() in part of your code. The problem is in your GET code--you are using render_to_response():

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

Instead use render():

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

See the example in the Django docs.

The reason you need to do that is because with csrf tokens you need to:

  1. Insert the csrf token in your form.

  2. Include the csrf token as a cookie in the headers of the request/response.

render() accomplishes #2, but render_to_response() does not--unless you specifically tell it to, which you did not. In any case, the django 1.9 docs state:

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.

Upvotes: 0

Pythonista
Pythonista

Reputation: 11635

Your problem is render_to_reponse. It doesn't have the context instance you can add it, but render handles this for you so why not just it instead. Also you can restructure your view to be a bit cleaner.

Here's one example.

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})

Then you can use {% if messages %} in your template to display your messages to the user / iterate over them and display.

messages here is from django.contrib so you'd need to do from django.contrib import messages

Upvotes: 1

aquaman
aquaman

Reputation: 451

Just modify your view.py like this

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))

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

Upvotes: 0

i.krivosheev
i.krivosheev

Reputation: 397

  1. Use render_to_response from django.shortcuts
  2. Use in render_to_response context_instance = RequestContext(request)

This must fix your problem with csrf token or read https://docs.djangoproject.com/ja/1.9/ref/csrf/

Upvotes: 0

Related Questions