I just set up an apache server with django, and to test it, made a very simple function in views.py
channel = rabbit_connection()
#csrf_protect
#csrf_exempt
def index(request):
data={'text': 'Food truck is awesome! ', 'email': 'bob#yahoo.com', 'name': 'Bob'}
callback(json.dumps(data))
context = RequestContext(request)
return render_to_response('index.html', context_instance=context)
This function works fine if I send a GET or POST request to the server. However I would like to get this data from POST request. Assuming I send request like this:
import pycurl
import simplejson as json
data = json.dumps({'name':'Bob', 'email':'bob#yahoo.com', 'text': u"Food truck is awesome!"})
c = pycurl.Curl()
c.setopt(c.URL, 'http://ec2-54-......compute-1.amazonaws.com/index.html')
c.setopt(c.POSTFIELDS, data)
c.setopt(c.VERBOSE, True)
for i in range(100):
c.perform()
What I would like to have in the view is something like this:
if request.method == 'POST':
data = ?????? # Something that will return me my dictionary
Just in case:
It is always will be in JSON format and the fields are unknown.
data= request.POST.get('data','')
Will return you a single value (key=data) from your dictionary. If you want the entire dictionary, you simply use request.POST. You are using the QueryDict class here:
In an HttpRequest object, the GET and POST attributes are instances of django.http.QueryDict. QueryDict is a dictionary-like class customized to deal with multiple values for the same key. This is necessary because some HTML form elements, notably , pass multiple values for the same key.
QueryDict instances are immutable, unless you create a copy() of them. That means you can’t change attributes of request.POST and request.GET directly.
-Django Docs
If the data posted is in JSON format, you need to deserialize it:
import simplejson
myDict = simplejson.loads(request.POST.get('data'))
Related
I'm using Django==1.10.5 and djangorestframework==3.5.3.
I have a few ModelViewSets that handle JSON API requests properly. Now, I'd like to use TemplateRenderer to add HTML rendering to those same ModelViewSets. I started with the list endpoint, and created a simple template that lists the available objects. I implemented the get_template_names to return the template I created.
Accessing that endpoint through the browser works fine when there are no objects to list, so everything related to setting up HTML renderers alongside APIs seems to work.However, when tere are objects to return the same endpoint fails with the following error:
ValueError: dictionary update sequence element #0 has length XX; 2 is required
Where XX is the number of attributes the object has.
This documentation section suggests the view function should act slightly differently when returning an HTML Response object, but I assume this is done by DRF's builtin views when necessary, so I don't think that's the issue.
This stackoverflow Q/A also looks relevant but I'm not quite sure it's the right solution to my problem.
How can I make my ModelViewSets work with both HTML and JSON renderers?
Thanks!
DRF has a brief explanation of how to do this in their documentation.
I think you'd do something like this...
On the client side, tell your endpoint what type of response you want:
fetch(yourAPIUrl, {
headers: {
'Accept': 'application/json'
// or 'Accept': 'text/html'
}
})
In your view, just check that and act accordingly:
class FlexibleAPIView(APIView):
"""
API view that can render either JSON or HTML.
"""
renderer_classes = [TemplateHTMLRenderer, JSONRenderer]
def get(self, request, *args, **kwargs):
queryset = Things.objects.all()
# If client wants HTML, give them HTML.
if request.accepted_renderer.format == 'html':
return Response({'things': queryset}, template_name='example.html')
# Otherwise, the client likely wants JSON so serialize the data.
serializer = ThingSerializer(instance=queryset)
data = serializer.data
return Response(data)
I'm using Django and have a view to get/insert some records in database. Here's my code:
class JSONResponse(HttpResponse):
"""
An HttpResponse that renders its content into JSON.
"""
def __init__(self, data, **kwargs):
content = JSONRenderer().render(data)
kwargs['content_type'] = 'application/json'
super(JSONResponse, self).__init__(content, **kwargs)
#api_view(('GET','POST'))
#renderer_classes((TemplateHTMLRenderer,))
#csrf_exempt
def cours_list(request):
if request.method == 'GET':
data = JSONParser().parse(request)
results = Lesson.objects.get(discipline=data.discipline)
return Response({'cours': results}, template_name='search_results.html')
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = LessonSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JSONResponse(serializer.data, status=201)
return JSONResponse(serializer.errors, status=400)
So to get data I use GET and to insert a new record I use POST. First, is this the right way to do ? I read once that it't a bad idea to use GET whatever the situation.
Also, the GET code is not actually working (I'm getting a bad request error) and it seems that this is comming from JSONParser that is not able to parse the request.
Edit1
Here is the request that is sent:
GET http://127.0.0.1:8000/cours/?{%22discipline%22:%22Mathematiques%22,%22localisation%22:%22%22}
Edit2
I tried to print requet.GET, and this what it gives:
<QueryDict: {'{"discipline":"Mathematiques","localisation":""}': ['']}>
When trying to access request.data['discipline'], it says:
django.utils.datastructures.MultiValueDictKeyError: "'discipline'"
Should I use GET or POST to retrieve results Django
To retrieve use a GET
I read once that it't a bad idea to use GET whatever the situation
Not at all, as long as your GET operation contains no side effects (e.g. in your GET method you are only reading from the database.
The GET code is not actually working (I'm getting a bad request error)
data2 = JSONParser().parse(request) # you used data2
results = Lesson.objects.get(discipline=data.discipline) # and you used data
^
|____ data? data2?
You're not passing the GET parameters correctly
You need to pass GET params like this url/?param1=value1
To read that value you use param1 = request.GET['param1'] and param1 will be the string value1
What you are doing isn't passing a value:
GET http://127.0.0.1:8000/cours/?{%22discipline%22:%22Mathematiques%22,%22localisation%22:%22%22}
That's why you get an empty value ['']
<QueryDict: {'{"discipline":"Mathematiques","localisation":""}': ['']}>
You can pass the entire data as a JSON string if you want, but I prefer to break it down like this:
url/?discipline=Mathematiques&localisation=Quelquechose
with
discipline = request.GET['discipline']
localisation = request.GET['localisation']
That's absolutely normally to use GET method for information retrieving, see HTTP/1.1: Method Dediniton, 9.3:
The GET method means retrieve whatever information (in the form of an
entity) is identified by the Request-URI. If the Request-URI refers to
a data-producing process, it is the produced data which shall be
returned as the entity in the response and not the source text of the
process, unless that text happens to be the output of the process.
For POST method, it's a good practice to submit new data with POST request, see HTTP/1.1: Method Dediniton, 9.5:
The POST method is used to request that the origin server accept the
entity enclosed in the request as a new subordinate of the resource
identified by the Request-URI in the Request-Line. POST is designed to
allow a uniform method to cover the following functions:
................................................................
- Providing a block of data, such as the result of submitting a
form, to a data-handling process;
- Extending a database through an append operation.
Also, take a look at Which HTTP methods match up to which CRUD methods? it will give you more clear idea of how and when particular HTTP methods shall be used and help you in next further development.
I have a django CMS page, I want the rendered html to be made into a json object(dump the CMS html into JSON), is this possible? I'd like it to look something like this:
How would I go about this?
So after some digging , this is what I managed.
CMS has a views.py which has a function "details(request, slug)"
I used this method to render the required page, by passing the slug of the page I need rendered , got the response and put it into a JSON object and returned this.
from cms.views import details
def index(request, topic):
if topic == 'home':
template = details(request, '')
else:
template = details(request, topic)
content = ''
if hasattr(template, 'render'):
# TemplateResponse must call render() before we can get the content
content = template.render().content
else:
# HttpResponse does not have a render() method
content = template.content
# Send JSON response
return JsonResponse({
'createdAt': datetime.datetime.now(),
'content': content
})
Any better way of doing the same?
I think this should be fairly easy. Instead of returning the rendered output from the template, put it in a dictionary. Then just from json import dumps or dump and dump(dictionary) / dumps(dictionary) depending on your purpose. dumps gives you a string. dump gives you something fancier.
I'm running into weird behavior when writing nested structures with django-rest and then trying to test them using django-rest's test client. The nested child object should be optional.
Here's a sample serializer:
from rest_framework import serializers
class OptionalChildSerializer(serializers.Serializer):
field_b = serializers.IntegerField()
field_c = serializers.IntegerField()
class Meta:
fields = ('field_b', 'field_c', )
class ParentSerializer(serializers.Serializer):
field_a = serializers.IntegerField()
child = OptionalChildSerializer(required=False, many=False)
class Meta:
fields = ('a', 'child',)
def perform_create(self, serializer):
# TODO: create nested object.
pass
(I've omitted the code in perform_create, as it's not relevant to the question).
Now, passing a normal dict as data argument works just fine:
ser = ParentSerializer(data=dict(field_a=3))
ser.is_valid(raise_exception=True)
But passing a QueryDict instead will fail:
from django.http import QueryDict
ser = ParentSerializer(data=QueryDict("field_a=3"))
ser.is_valid(raise_exception=True)
ValidationError: {'child': {'field_b': [u'This field is required.'], 'field_c': [u'This field is required.']}}
On the actual web site, the API gets a normal dict and thus works fine. During testing however, using something like client.post('url', data=dict(field_a=3)) will result in a QueryDict being passed to the view, and hence not work.
So my question is: what's the difference between the QueryDict and normal dict? Or am I approaching this the wrong way?
DRF defines multiple parser classes for parsing the request content having different media types.
request.data will normally be a QueryDict or a normal dictionary depending on the parser used to parse the request content.
JSONParser:
It parses the JSON request content i.e. content with .media_type as application/json.
FormParser
It parses the HTML form content. Here, request.data is populated with a QueryDict of data. These have .media_type as application/x-www-form-urlencoded.
MultiPartParser
It parses multipart HTML form content, which supports file uploads. Here also, both request.data is populated with a QueryDict. These have
.media_type as multipart/form-data.
FileUploadParser
It parses raw file upload content. The request.data property is a dictionary with a single key file containing the uploaded file.
How does DRF determines the parser?
When DRF accesses the request.data, it examines the Content-Type header on the incoming request and then determines which parser to use to parse the request content.
You will need to set the Content-Type header when sending the data otherwise it will use either a multipart or a form parser to parse the request content and give you a QueryDict in request.data instead of a dictionary.
As per DRF docs,
If you don't set the content type, most clients will default to using
'application/x-www-form-urlencoded', which may not be what you wanted.
So when sending json encoded data, also set the Content-Type header to application/json and then it will work as expected.
Why request.data is sometimes QueryDict and sometimes dict?
This is done because different encodings have different datastructures and properties.
For example, form data is an encoding that supports multiple keys of the same value, whereas json does not support that.
Also in case of JSON data, request.DATA might not be a dict at all, it could be a list or any of the other json primitives.
Check out this Google Groups thread about the same.
What you need to do?
You can add format='json' in the tests when POSTing the data which will set the content-type as well as serialize the data correctly.
client.post('url', format='json', data=dict(field_a=3))
You can also send JSON-encoded content with content-type argument.
client.post('url', json.dumps(dict(field_a=3)), content_type='application/json')
The django-rest test client doesn't automatically serialize data as json, but uses multipart/form, which results in a QueryDict.
There is, however, a format option, described in the docs. The following test code works fine:
client.post('url', format='json', data=dict(field_a=3))
I'm still puzzled on the different serializer behavior between a normal dict and a QueryDict, though...
Thanks Rajesh for pointing me in the right direction!
Is there a way to get the form errors generated during the django form validation in a dictionary (key as the 'field_name' and value as 'the list of errors relevant to it'), instead of the default HTML code it generates (ul & li combination). I'm not using the generated HTML code, and I'm just bothered about the field name and the errors.
There's not built-in method for return errors as dict, but you can use json.
form = MyForm(data)
print(form.errors.as_json())
doc for errors.as_json
Sure. I use this class often when doing forms that are Ajaxed and I need to return JSON instead. Tweak/improve as necessary. In some cases, you might want to return HTML encoded in the JSON, so I pass in the stripping of HTML tags as an option.
from django import forms
from django.template.defaultfilters import striptags
class AjaxBaseForm(forms.BaseForm):
def errors_as_json(self, strip_tags=False):
error_summary = {}
errors = {}
for error in self.errors.iteritems():
errors.update({error[0]: unicode(striptags(error[1])
if strip_tags else error[1])})
error_summary.update({'errors': errors})
return error_summary
Usage:
# forms.py
class MyForm(AjaxBaseForm, forms.Form): # you can also extend ModelForm
...
# views.py
def my_view(request):
form = MyForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
...
else:
response = form.errors_as_json(strip_tags=True)
return HttpResponse(json.dumps(response, ensure_ascii=False),
content_type='application/json')
Calling dict() worked for me:
print(dict(form.errors))
I prefer this technique because as_json() converts special characters to Unicode escape sequence.
In the view you can do this:
f = FooForm(data)
f.errors
f.errors.as_json()
Use this method anytime you need to identify an error by its code. This enables things like rewriting the error’s message or writing custom logic in a view when a given error is present. It can also be used to serialize the errors in a custom format (e.g. XML); for instance, as_json() relies on as_data().