51

Since Django 1.5 raw post data is accessible via request.body.

In my application I sometimes get data send via a form and sometimes raw data (json for example). Is there any way to write a function like this that does not fail?

def get_post_var(request, name):
    result = request.POST.get(name)
    if result:
        return result

    post_body = dict(urlparse.parse_qsl(request.body))
    result = post_body.get(name)
    if result:
        return result

    return None
kev
  • 8,928
  • 14
  • 61
  • 103
  • What do you mean by "sometimes plain data". If its a POST request, django will take care of populating request.POST, irrespective of whether the data is submitted via a form or through curl or anything else. – Akshar Raaj Oct 25 '13 at 06:06
  • 2
    I mean non-form data (json for example) as described here: https://docs.djangoproject.com/en/1.5/ref/request-response/#django.http.HttpRequest.POST – kev Oct 25 '13 at 06:27

7 Answers7

70

Use request.data instead of request.body.

request.data does not read the data stream again.

Shadab Ahmed
  • 556
  • 9
  • 20
Arnab Biswas
  • 902
  • 7
  • 8
  • 5
    This is the actual answer that fixes the error. Should have higher upvotes. – Tim Hong Jul 23 '20 at 11:38
  • 11
    I get `AttributeError: 'WSGIRequest' object has no attribute 'data'` in Django 1.4 using `request.data`. Some idea about how can I resolve that? – Carmoreno Sep 15 '20 at 19:46
  • 8
    @CarMoreno @Ulvi I believe `request.data` is a DRF thing. The `request` object that is passed to DRF views, is different to the `request` object passed to normal django views. – tim-mccurrach Nov 15 '20 at 12:41
  • 1
    @TimHong this fixes the problem _when_ the problem is caused by DRF, but 1) this is not always the case, 2) it's not explained in the answer and confuses people, 3) nowhere in the question it's mentioned DRF. Sure, probably 90% of the time this is caused by DRF, but Adam's answer is the most general and correct explanation which applies in any case. – sox supports the mods Feb 21 '22 at 16:24
  • `WSGIRequest object has no attribute data` with `django 4`. – ar2015 Jan 23 '23 at 04:52
51

The error You cannot access body after reading from request's data stream will be triggered on a request if (1) that request method is POST, (2) that request's POST dictionary is accessed in middleware, in either process_request or process_view and (3) within the view function, request.body is accessed. It is on (3) that the error will be raised, even though the real cause of the bug is (2).

In order to resolve the error, you need to examine your middleware for where it accesses request.POST and modify it such that it doesn't access request.POST anymore.

The Django docs say that middleware should not access request.POST, and this is one consequence of ignoring that recommendation.

Also check out this Django ticket on the issue, which includes the note:

[M]iddleware that hits request.POST should (usually) be considered a bug. It means that the view will be unable to set any custom upload handlers, perform custom parsing of the request body, or enforce permission checks prior to file uploads being accepted.

Adam Easterling
  • 2,266
  • 1
  • 21
  • 21
14

Adding to Adam Easterling's answer it is worth noting that Django itself 'violates' the hint of not using request.POST in middleware:

The CsrfViewMiddleware class can be considered an exception, as it provides the csrf_exempt() and csrf_protect() decorators which allow views to explicitly control at what point the CSRF validation should occur.

Which does not sanitilize the violation IMO

Unicorn
  • 1,397
  • 1
  • 15
  • 24
  • 3
    Indeed this was an annoyance for me. The solution/workaround is to use PUT rather than POST if you mean to read request body (other than through the POST/FILES). – Amichai Schreiber May 23 '17 at 08:56
3

For those interested to know, I faced this issue:

You cannot access body after reading from request's data stream

when I added 'oauth2_provider.contrib.rest_framework.OAuth2Authentication' in the "REST_FRAMEWORK" like so in the settings.py:

REST_FRAMEWORK = {
   ...
    'DEFAULT_AUTHENTICATION_CLASSES': (
      ...
       'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
      ...
    ),

Of course disabling this will work but not a workaround I would be proud of.

fedorqui
  • 275,237
  • 103
  • 548
  • 598
AwsAnurag
  • 91
  • 7
  • 1
    You must explicitly use `authentication_classes = []` at the top of your APIView to solve this issue – fredperk Nov 23 '20 at 13:22
0

I was able to read my request.POST after putting @csrf_exempt before my view function. Because CSRF middleware accesses POST data.

Siavoshkc
  • 346
  • 2
  • 16
-1

For those with the same error who are not readying the body or POST, I had this same error when I used this line of code in a process_view middleware::

   event = request.event if 'event' in request else None

Solved by settings request.event = None at the top of the function so I could then use:

    event = request.event
PhoebeB
  • 8,434
  • 8
  • 57
  • 76
-1

I found a trick, using for my middleware,

request._read_started = False

after doing that, ready body again and it works.