POSTing from Angular to Django
If you are using Django and AngularJS then you have probably run into the issue of trying to POST data to Djan go. For example,
$http({
url: "<myawesomeview>",
method: 'POST',
data: { 'text': 'this is really important', 'date': '2014-06-09'}
})
The issue that arises is that AngularJS defaults to sending JSON data in the response body instead of urlencoded data, which is the type of data that Django is looking for. As a result, back in Django if we did the following
def myawesomeview(request):
print request.POST
print request.body
We would get
<QueryDict: {}>
u"{ 'text': 'this is really important', 'date': '2014-06-09'}"
back out on our console. If you have been using Django for even a little bit, then you realize that we were really expecting
<QueryDict: {u'text': u'this is really important', u'date': u'2014-06-09'}>
u"{ 'text': 'this is really important', 'date': '2014-06-09'}"
You can find my solution to this in this gist, which is kindly provided below:
UPDATE I found a bug in my previous version, I have updated it to address this, the primary issue was how list value were being handled.
class JSONMiddleware(object):
"""
Process application/json requests data from GET and POST requests.
"""
def process_request(self, request):
if 'application/json' in request.META['CONTENT_TYPE']:
# load the json data
data = json.loads(request.body)
# for consistency sake, we want to return
# a Django QueryDict and not a plain Dict.
# The primary difference is that the QueryDict stores
# every value in a list and is, by default, immutable.
# The primary issue is making sure that list values are
# properly inserted into the QueryDict. If we simply
# do a q_data.update(data), any list values will be wrapped
# in another list. By iterating through the list and updating
# for each value, we get the expected result of a single list.
q_data = QueryDict('', mutable=True)
for key, value in data.iteritems():
if isinstance(value, list):
# need to iterate through the list and upate
# so that the list does not get wrapped in an
# additional list.
for x in value:
q_data.update({key: x})
else:
q_data.update({key: value})
if request.method == 'GET':
request.GET = q_data
if request.method == 'POST':
request.POST = q_data
return None
The old naive version
class JSONMiddleware(object):
"""
Process application/json requests data from GET and POST requests.
"""
def process_request(self, request):
if 'application/json' in request.META['CONTENT_TYPE']:
data = urlencode(json.loads(request.body))
if request.method == 'GET':
request.GET = QueryDict(data)
if request.method == 'POST':
request.POST = QueryDict(data)
return None
If you add this middleware to your Django install, it will process the JSON data and put it in the GET or POST variable as appropriate. There are, of course, other solutions to this problem, but using this middleware allows you to write you AngularJS or your Django views as intended, passing an object as the data and reading from request.POST.
Written by Lucas
Related protips
3 Responses
Nice simple workaround. If you do a lot of REST calls with Django, you should also checkout the Django Rest Framework which enable all this natively, and much more!
@yannikmesserli, that is a really good point. I found this is really useful for those times when I need to write a small little function based view that doesn't need the overhead or is more complex to try to write in the Django Rest Framework. This mostly happened when I was going in an optimizing certain sections that needed to be as fast as possible.
Here is my code:
class JsonPayloadToRequestDataMiddleware(object):
def process_request(self, request):
if request.method != "POST":
return None
if 'application/json' not in request.META['CONTENT_TYPE']:
return None
body = request.body.strip()
if not body:
return None
is_obj = body.startswith("{") and body.endswith("}")
is_array = body.startswith("[") and body.endswith("]")
if not is_obj and not is_array:
return None
# data is django.http.QueryDict
json_data = json.loads(request.body)
string_data = urllib.urlencode(json_data, True)
data = QueryDict(string_data, mutable=True)
if request.POST:
data.update(request.POST)
# it should be immutable
request.POST = data.copy()