how to get POST data in django 1.3
Asked Answered
K

4

9

Hey, I am following this tutorial to learn to make a wiki page with Django. However, it is made in django 0.96 and I use Django 1.3 so there are some things that are different. Some I already fixed myself, however this one I can't seem to make it work.

I made a form that submits data to a view. This is the form:

<form method="post" action"/wikicamp/{{page_name}}/save/">{% csrf_token %}
    <textarea name="content" rows="20" cols="60">{{content}}</textarea><br>
    <input type="submit" value="Save Page"/>
</form>

and the /wikicamp/{{page_name}}/save/ url redirects to the save_page view:

from django.http import HttpResponseRedirect
from django.core.context_processors import csrf

def save_page(request, page_name):
    c = {}
    c.update(csrf(request))
    content = c.POST["content"]
    try:
        page = Page.objects.get(pk=page_name)
        page.content = content
    except Page.DoesNotExist:
        page = Page(name=page_name, content=content)
    page.save()
    return HttpResponseRedirect("wikicamp/" + page_name + "/")

However the problem is that I get this error:

Help

Reason given for failure:

    CSRF token missing or incorrect.


In general, this can occur when there is a genuine Cross Site Request Forgery, or when Django's CSRF mechanism has not been used correctly. For POST forms, you need to ensure:

    The view function uses RequestContext for the template, instead of Context.
    In the template, there is a {% csrf_token %} template tag inside each POST form that targets an internal URL.
    If you are not using CsrfViewMiddleware, then you must use csrf_protect on any views that use the csrf_token template tag, as well as those that accept the POST data.

You're seeing the help section of this page because you have DEBUG = True in your Django settings file. Change that to False, and only the initial error message will be displayed.

You can customize this page using the CSRF_FAILURE_VIEW setting.

So I read through some of the documentation, like http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#how-to-use-it. I tried to do that however and it still gave the same error.

So: Anyone an idea how to handle form post data well with Django 1.3?

I think it has something to do with: The view function uses RequestContext for the template, instead of Context. but i don't now what it is.

btw, in my terminal which shows the http request of the localhost it says this: A {% csrf_token %} was used in a template, but the context did not provide the value. This is usually caused by not using RequestContext.

Kruller answered 16/5, 2011 at 17:17 Comment(0)
I
9

You've got to include {% csrf_token %} in your form's template between your <form> tags.

<form method="post" action"/wikicamp/{{page_name}}/save/">
    {% csrf_token %}
    <textarea name="content" rows="20" cols="60">{{content}}</textarea><br>
    <input type="submit" value="Save Page"/>
</form>

If the csrf_token is not rendered into your form make sure you're providing the RequestContext in the view's response:

from django.shortcuts import render_to_response
from django.template import RequestContext

def app_view(request):
    return render_to_response('app_template.html', 
                              app_data_dictionary, 
                              context_instance=RequestContext(request))

Or, use this shortcut method:

from django.views.generic.simple import direct_to_template

def app_view(request):             
    return direct_to_template(request, 'app_template.html', app_data_dictionary)

The RequestContext is always available when you're using generic views.

Iridotomy answered 16/5, 2011 at 17:21 Comment(15)
that doesn't work, I did that already before asking over here. I did the things described in the last link of the docs. Any idea?Kruller
Did you reload the page before submitting it again? You can view the source to verify that the CSRF token is actually rendered in the form.Iridotomy
Taking a closer look, do you have have django.middleware.csrf.CsrfViewMiddleware in your MIDDLEWARE_CLASSES in settings.py?Iridotomy
yes i did, the csrf token isn't renderen in the form. But how could including that token possibly solve all my problems with CSRF?Kruller
You could also try and delete your cookies.Iridotomy
already tried in different browsers as well as deleted cookies.Kruller
maybe it is the HttpResponseRedirect i use? Or is this wrong: c = {} c.update(csrf(request)) content = c.POST["content"]Kruller
Perhaps it's the order of the middleware?Iridotomy
tried both 'django.middleware.csrf.CsrfResponseMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', and the other way around.Kruller
What I mean is, the order in which the middleware is loaded: MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', )Iridotomy
I tried first with the response before the view, than the view before the response. Is that what you mean?Kruller
I updated my view in the question with c.update(csrf(request)) content = c.POST["content"] , is that the okay way to do it?Kruller
If you really want to get around CSRF verification you can skip it all together by decorating a view with @csrf_exemptIridotomy
I think the problem is that I am not using RequestContext? As it says that I need to make sure: The view function uses RequestContext for the template, instead of Context.Kruller
Ah, yes. Absolutely. Either use direct_to_template or inject the request context in your response.Iridotomy
T
9

You will need the {% csrf_token %} template tag in between your tags as well as including

   django.middleware.csrf.CsrfViewMiddleware
   django.middleware.csrf.CsrfResponseMiddleware

in your MIDDLEWARE_CLASSES in the applications settings.py

Adding some example post data handling:

This is an example of one of the times I am using POST data in a view. I will generally rely on the form class to do verification via the cleaned_data array.

if request.method == 'POST':
        form = ForgotPassword(data=request.POST)
        if form.is_valid():
            try:
                new_user = backend.forgot_password(request, **form.cleaned_data)
            except IntegrityError:
                context = {'form':form}
                form._errors[''] = ErrorList(['It appears you have already requested a password reset, please \
                check ' + request.POST['email2'] + ' for the reset link.'])
                return render_template(request,'passwordReset/forgot_password.html',context)
            if success_url is None:
                to, args, kwargs = backend.post_forgot_password(request, new_user)
                return redirect(to, *args, **kwargs)
            else:
                return redirect(success_url)
Teodoor answered 16/5, 2011 at 17:35 Comment(9)
if i add that one and i click on the form button no error occurs, but the form page just reloads emptying the textarea field while nothing happens. (not the redirect i want to anyway)Kruller
I updated my view in the question with c.update(csrf(request)) content = c.POST["content"] , is that the okay way to do it?Kruller
is debugging turned on? if so can you post the stack trace?Teodoor
I do not believe you necessarily have to use the token, I ran into this a few months ago upgrading from 1.1 to 1.3 and all I had to do was add the middleware entries and the csrf_tokenTeodoor
I updated my wuestion with the whole error. If this is not a stack trace, please explain me how i could get it. thanks!Kruller
btw, in my terminal which shows the http request of the localhost it says this: A {% csrf_token %} was used in a template, but the context did not provide the value. This is usually caused by not using RequestContext.Kruller
to show the stack trace you would set DEBUG = True in the settings.py . I am trying to get the CSRF error myself again but not having any luck for some reason. Nevermind, just got it and it does not print out anything else of use to us here.Teodoor
You need to look at docs.djangoproject.com/en/dev/ref/templates/api/… . That link will explain how to use RequestContext instead of context in your view. This is the second link in the 403 page you are gettingTeodoor
thanks. I read that, but I don't use the Context class at this moment, or do I?Kruller
S
2

I guess you've missed the symbol '=' in the form declaration.

action"/wikicamp/{{page_name}}/save/"

action="/wikicamp/{{page_name}}/save/"

Fortunately, it might be not a mistake. So if it is not a solution, try some more easy example:

# settings.py

TEMPLATE_DIRS = (
    # Here comes something like "C:/www/django/templates"
)

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

# urls.py

urlpatterns = patterns('',
    ('^foo', foo),
)


# views.py
from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.core.context_processors import csrf

def foo(request):
    d = {}
    d.update(csrf(request))
    if 'output' in request.POST:
        d.update({'output':request.POST['output']})
    return render_to_response('foo.html',d)

# foo.html template
<html>
<h1> Foo </h1>
<form action="/foo" method = "post">
    {% csrf_token %}
    <input type="text" name="output"></input>
    <input type="submit" value="go"></input>
</form>
<p> Output: {{ output }} </p>
</html>

Hope this will work

Swum answered 17/5, 2011 at 7:53 Comment(0)
R
2

re above use "request.POST" not "c.POST" in the 3rd line

def save_page (request,page_name):
    content = request.POST["content"]

and change in "edit_page"

-   return render_to_response("edit.html",{"page_name":page_name, "content":content})
+   t = get_template('edit.html')
+   html = t.render(Context({"page_name":page_name, "content":content}))
+   return HttpResponse(html)

- :remove 
+ :add
Rambunctious answered 10/8, 2011 at 14:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.