Prevent Django's JsonResponse to serialize my string - python

I have a JSON string that I compute from a Pandas dataframe
aggr.aggregated.to_json(orient='values')
I cannot directly provide aggr.aggregated to a standard Python JSON serializer because it would not follow the orient='values' rules and would do so differently.
I want to serve my own JSON string as a response from a Django view:
return JsonResponse(aggr.aggregated.to_json(orient='values'))
However, in the code above, Django tried to serialize my JSON string.
How can I use JsonResponse exclusively to set the Content-Type header to application/json but not to serialize a string that is already serialized?

There is no benefit in using JsonResponse if you don't want it to encode the JSON for you.
Just use HttpResponse and set the content-type header yourself:
return HttpResponse(
aggr.aggregated.to_json(orient='values'),
content_type='application/json'
)

Related

How to get query params in fast api from POST request

Client might send multiple query params like:
# method = POST
http://api.com?x=foo&y=bar
I need to get all the query params and extract it as a string x=foo&y=bar from the POST request.
Is there a way to do this on fast api? I could not find it on their docs.
NOTE :
We are not sure of the names of the params before hand.
Depending how you want to use the data, you can either use request.query_params to retrieve a immutable dict with the parameters, or you can use request.url to get an object representing the actual URL. This object has a query property that you can use to get the raw string:
from fastapi import FastAPI, Request
import uvicorn
app = FastAPI()
#app.get('/')
def main(request: Request):
return request.url.query
uvicorn.run(app)
This returns the given query string /?foo=123 as foo=123 from the API. There is no difference between a POST or GET request for this functionality.

Does Django automatically URL decode values

If I have a django app that accepts data at /XXXX/, will Django pass me (in the view) the url-decoded or url-encoded data?
Mock-up Example:
def view(req, s):
return HttpResponse("Is this URL-decoded? : " + s)
urlpatterns = [
...,
path('<str:s>/', view),
...,
]
The answer is No. Django passed the url "AS IS" and DO NOT encodes or decodes it on its own.
For HttpResponse object, Based on this:
In contrast to HttpRequest objects, which are created automatically by Django, HttpResponse objects are your responsibility. Each view you write is responsible for instantiating, populating, and returning an HttpResponse.
from here, Django sets the parameter values exactly the same as what you provide (you may refer this as "the decoded one"). If you need to decode the url, you can use the code below (Python 3+):
from urllib.parse import unquote
url = "http://..."
urllib.unquote(url)
And for encoding:
from django.utils.http import urlencode
urlencode(url)

django - post data query dict is empty

i use postman app and set content-type : application/json
and using post method
in the body i select "raw" and "JSON(application/json)"
and after all of this i enter a simple json :
{"token":"ghdfhldfigpd","text":"test Expense" ,"amount":10000}
but when i debug my django app : query dict is empty see this picture
but when i enter my data in form-data section
my app works and post query dict is not empty
what the problem should be ?
EDIT : i see that the data goes to body but not to post query dict:
this picture
request.POST is only for application/x-www-form-urlencoded data. Since you are using json, you should use request.body with json.loads.
import json
def my_view(request):
data = json.loads(request.body.decode('utf-8'))
...

django-rest-framework: Rendering both HTML and JSON with the same ModelViewSet

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)

Serializing optionally nested structures: Difference between QueryDict and normal dict?

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!

Categories