203

I'm trying to process incoming JSON/Ajax requests with Django/Python.

request.is_ajax() is True on the request, but I have no idea where the payload is with the JSON data.

request.POST.dir contains this:

['__class__', '__cmp__', '__contains__', '__copy__', '__deepcopy__', '__delattr__',
 '__delitem__', '__dict__', '__doc__', '__eq__', '__ge__', '__getattribute__',
'__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__',
 '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', 
'__setattr__', '__setitem__', '__str__', '__weakref__', '_assert_mutable', '_encoding', 
'_get_encoding', '_mutable', '_set_encoding', 'appendlist', 'clear', 'copy', 'encoding', 
'fromkeys', 'get', 'getlist', 'has_key', 'items', 'iteritems', 'iterkeys', 'itervalues', 
'keys', 'lists', 'pop', 'popitem', 'setdefault', 'setlist', 'setlistdefault', 'update', 
'urlencode', 'values']

There are apparently no keys in the request post keys.

When I look at the POST in Firebug, there is JSON data being sent up in the request.

Flimm
  • 136,138
  • 45
  • 251
  • 267

13 Answers13

264

If you are posting JSON to Django, I think you want request.body (request.raw_post_data on Django < 1.4). This will give you the raw JSON data sent via the post. From there you can process it further.

Here is an example using JavaScript, jQuery, jquery-json and Django.

JavaScript:

var myEvent = {id: calEvent.id, start: calEvent.start, end: calEvent.end,
               allDay: calEvent.allDay };
$.ajax({
    url: '/event/save-json/',
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    data: $.toJSON(myEvent),
    dataType: 'text',
    success: function(result) {
        alert(result.Result);
    }
});

Django:

def save_events_json(request):
    if request.is_ajax():
        if request.method == 'POST':
            print 'Raw Data: "%s"' % request.body   
    return HttpResponse("OK")

Django < 1.4:

  def save_events_json(request):
    if request.is_ajax():
        if request.method == 'POST':
            print 'Raw Data: "%s"' % request.raw_post_data
    return HttpResponse("OK")
Thomas Orozco
  • 53,284
  • 11
  • 113
  • 116
Jared Knipp
  • 5,880
  • 7
  • 44
  • 52
  • Please explain what you mean by 'test client'? What are you trying to do? – Jared Knipp Oct 25 '11 at 19:00
  • I'm not trying to be rude: By "test client," I mean the django "test client." How do you test views if not with the test client? – jMyles Nov 02 '11 at 20:52
  • 4
    Take in mind: You should end url with slash ( / ) char. Also disable CSRF with @csrf_exempt – dani herrera Jun 03 '12 at 19:36
  • 47
    NB if you're using 1.4 this would be called request.body . raw_post_data is deprecated... – prauchfuss Oct 08 '12 at 03:46
  • 3
    to test with django unittest just do `self.client.post('/event/save-json/', json.dumps(python_dict), HTTP_X_REQUESTED_WITH='XMLHttpRequest', content_type="application/json")` – Guillaume Vincent Aug 11 '13 at 20:12
  • Note that this Django code always returns a 200 response, even when the request method is wrong. I don't mean to be a smartarse, but I see engineers do this in real-life code all the time, and it makes front-end coding more difficult. In this case, `return HttpResponseBadRequest()` and `return HttpResponseNotAllowed(['POST'])` are appropriate `else` clauses. – Michael Scheper Sep 25 '18 at 08:41
  • Doesn't really answer Op's question. He wants the JSON data, not a string of the JSON data, which is what's stored in request.body. You didn't mention that Op will need to parse that string. – Cerin May 19 '22 at 04:02
91

I had the same problem. I had been posting a complex JSON response, and I couldn't read my data using the request.POST dictionary.

My JSON POST data was:

//JavaScript code:
//Requires json2.js and jQuery.
var response = {data:[{"a":1, "b":2},{"a":2, "b":2}]}
json_response = JSON.stringify(response); // proper serialization method, read 
                                          // http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
$.post('url',json_response);

In this case you need to use method provided by aurealus. Read the request.body and deserialize it with the json stdlib.

#Django code:
import json
def save_data(request):
  if request.method == 'POST':
    json_data = json.loads(request.body) # request.raw_post_data w/ Django < 1.4
    try:
      data = json_data['data']
    except KeyError:
      HttpResponseServerError("Malformed data!")
    HttpResponse("Got json data")
mrooney
  • 1,994
  • 2
  • 19
  • 30
stricjux
  • 1,394
  • 12
  • 13
  • 2
    I am having issues with the 4th line: `json_data = simplejson.loads(request.raw_post_data)` are you sure that correctly stated? – wfbarksdale Nov 29 '11 at 01:05
  • I'm quite sure that the request.raw_post_data is the correct form as I did use this example in testing. What kind of issues do you have @weezybizzle? – stricjux Nov 29 '11 at 16:02
  • 1
    The data coming in some extra text appended too it that was screwing up the parsing. So it was 100% me. – wfbarksdale Nov 29 '11 at 16:07
  • 4
    `django.utils.simplejson` has been removed in recent versions. Just use the stdlib `json` library. – Martijn Pieters Jun 18 '14 at 12:23
  • You'll want to use request.body instead of request.raw_post_data for Django 1.4+ – mrooney May 10 '16 at 20:59
46

Method 1

Client : Send as JSON

$.ajax({
    url: 'example.com/ajax/',
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    processData: false,
    data: JSON.stringify({'name':'John', 'age': 42}),
    ...
});

//Sent as a JSON object {'name':'John', 'age': 42}

Server :

data = json.loads(request.body) # {'name':'John', 'age': 42}

Method 2

Client : Send as x-www-form-urlencoded
(Note: contentType & processData have changed, JSON.stringify is not needed)

$.ajax({
    url: 'example.com/ajax/',
    type: 'POST',    
    data: {'name':'John', 'age': 42},
    contentType: 'application/x-www-form-urlencoded; charset=utf-8',  //Default
    processData: true,       
});

//Sent as a query string name=John&age=42

Server :

data = request.POST # will be <QueryDict: {u'name':u'John', u'age': 42}>

Changed in 1.5+ : https://docs.djangoproject.com/en/dev/releases/1.5/#non-form-data-in-http-requests

Non-form data in HTTP requests :
request.POST will no longer include data posted via HTTP requests with non form-specific content-types in the header. In prior versions, data posted with content-types other than multipart/form-data or application/x-www-form-urlencoded would still end up represented in the request.POST attribute. Developers wishing to access the raw POST data for these cases, should use the request.body attribute instead.

Probably related

user
  • 17,781
  • 20
  • 98
  • 124
  • 3
    Re 1 - `django.http.request.RawPostDataException: You cannot access body after reading from request's data stream` – AlxVallejo Mar 15 '18 at 21:28
28

Its important to remember Python 3 has a different way to represent strings - they are byte arrays.

Using Django 1.9 and Python 2.7 and sending the JSON data in the main body (not a header) you would use something like:

mydata = json.loads(request.body)

But for Django 1.9 and Python 3.4 you would use:

mydata = json.loads(request.body.decode("utf-8"))

I just went through this learning curve making my first Py3 Django app!

Richard Cooke
  • 713
  • 8
  • 15
  • 4
    Thank you for your explanation! I'm using Django 1.10 and Python 3.5, mydata = json.loads(request.body.decode("utf-8")) works! – Julia Zhao Apr 16 '17 at 18:47
23

request.raw_response is now deprecated. Use request.body instead to process non-conventional form data such as XML payloads, binary images, etc.

Django documentation on the issue.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kevin S Lin
  • 529
  • 8
  • 14
9

on django 1.6 python 3.3

client

$.ajax({
    url: '/urll/',
    type: 'POST',
    contentType: 'application/json; charset=utf-8',
    data: JSON.stringify(json_object),
    dataType: 'json',
    success: function(result) {
        alert(result.Result);
    }
});

server

def urll(request):

if request.is_ajax():
    if request.method == 'POST':
        print ('Raw Data:', request.body) 

        print ('type(request.body):', type(request.body)) # this type is bytes

        print(json.loads(request.body.decode("utf-8")))
Rubber Duck
  • 3,673
  • 3
  • 40
  • 59
  • request.is_ajax() removed in Django 4+ https://docs.djangoproject.com/en/4.0/releases/3.1/#id2 – Ryan Apr 30 '23 at 17:40
6

Something like this. It's worked: Request data from client

registerData = {
{% for field in userFields%}
  {{ field.name }}: {{ field.name }},
{% endfor %}
}


var request = $.ajax({
   url: "{% url 'MainApp:rq-create-account-json' %}",
   method: "POST",
   async: false,
   contentType: "application/json; charset=utf-8",
   data: JSON.stringify(registerData),
   dataType: "json"
});

request.done(function (msg) {
   [alert(msg);]
   alert(msg.name);
});

request.fail(function (jqXHR, status) {
  alert(status);
});

Process request at the server

@csrf_exempt
def rq_create_account_json(request):
   if request.is_ajax():
       if request.method == 'POST':
           json_data = json.loads(request.body)
           print(json_data)
           return JsonResponse(json_data)
   return HttpResponse("Error")
Nghia Tu
  • 61
  • 1
  • 2
5

The HTTP POST payload is just a flat bunch of bytes. Django (like most frameworks) decodes it into a dictionary from either URL encoded parameters, or MIME-multipart encoding. If you just dump the JSON data in the POST content, Django won't decode it. Either do the JSON decoding from the full POST content (not the dictionary); or put the JSON data into a MIME-multipart wrapper.

In short, show the JavaScript code. The problem seems to be there.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Javier
  • 60,510
  • 8
  • 78
  • 126
  • I see the problem now! The type='json' parameter in jquery refers to what type to expect, not what it sends. It's sending regular form post encoded data, so if I want to send "json" I need to somehow convert it into a string, and pass "json={foo:bar, }" etc I can't believe, however, that that's how most people do it. I must be missing something here. –  Jul 30 '09 at 18:18
  • Actually you can convert the form to a JSON string in jQuery with the .serialize() function. But why do you particularly need to send json, though? What's wrong with just sending the form data? – Daniel Roseman Jul 30 '09 at 18:45
  • 4
    There are many cases where raw form data isn't enough; JSON allows you to send hierarchical objects, not just key : value pairs. You can send nested sets, arrays, etc. You could probably do all of that with post data, but it's not as convenient. It's kinda nice to just always deal with JSON, both to and from – taxilian May 15 '10 at 20:03
5

request.raw_post_data has been deprecated. Use request.body instead

Andres
  • 2,880
  • 4
  • 32
  • 38
2
html code 

file name  : view.html


    <!DOCTYPE html>
    <html>
    <head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script>
    $(document).ready(function(){
        $("#mySelect").change(function(){
            selected = $("#mySelect option:selected").text()
            $.ajax({
                type: 'POST',
                dataType: 'json',
                contentType: 'application/json; charset=utf-8',
                url: '/view/',
                data: {
                       'fruit': selected
                      },
                success: function(result) {
                        document.write(result)
                        }
        });
      });
    });
    </script>
    </head>
    <body>

    <form>
        <br>
    Select your favorite fruit:
    <select id="mySelect">
      <option value="apple" selected >Select fruit</option>
      <option value="apple">Apple</option>
      <option value="orange">Orange</option>
      <option value="pineapple">Pineapple</option>
      <option value="banana">Banana</option>
    </select>
    </form>
    </body>
    </html>

Django code:


Inside views.py


def view(request):

    if request.method == 'POST':
        print request.body
        data = request.body
        return HttpResponse(json.dumps(data))
Rajan Mandanka
  • 2,003
  • 21
  • 13
0

If you have set rest_framework.parsers.JSONParser in your django settings Then your json will be in the data attribute of request object.

To access it:

def post(self, request):
    json_data = request.data

Here is a Similar Answer

avvijeet
  • 41
  • 1
  • 4
-3

Using Angular you should add header to request or add it to module config headers: {'Content-Type': 'application/x-www-form-urlencoded'}

$http({
    url: url,
    method: method,
    timeout: timeout,
    data: data,
    headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
Opal
  • 81,889
  • 28
  • 189
  • 210
-4

request.POST is just a dictionary-like object, so just index into it with dict syntax.

Assuming your form field is fred, you could do something like this:

if 'fred' in request.POST:
    mydata = request.POST['fred']

Alternately, use a form object to deal with the POST data.