Django Rest test not giving XML as response but json - python

This is my view
from rest_framework_xml.renderers import XMLRenderer
#api_view(['GET', 'PUT', 'POST'])
#renderer_classes((XMLRenderer,))
#permission_classes((AllowAny,))
def users(request, id, format=None):
serializer = MySerializer(model, context={'request': request})
return Response(serializer.data)
If i visit the URL by browser then i get XML as response in chrome as desired.
But in my tests
url = reverse('user-detail', kwargs={
'id': 10,
})
response = self.client.get(url)
result = response.data
I get the result as dictionary . I mean the result which i get before passing to XMLRender.
I don't know if thats the desired behaviour but how can test the xml response

The .data property of the Response holds the unrendered content. You want to access .content to see your rendered XML.

Related

access data from request body django

I am using django v2.2.4 and need to access request body data.
Here's my code:
#api_view(['POST'])
#renderer_classes((JSONRenderer,))
def index(request):
if request.method == 'POST':
results= []
data = JSONParser().parse(request)
serializer = ScrapeSerializer(data=data)
if serializer.is_valid():
url = request.data.url
#url = request.POST.get('url')
But I get this error:
RawPostDataException at /scrape/
You cannot access body after reading from request's data stream
Here's the request body:
{
"url": "xyz.com"
}
How can I access the request body?
I have found this SO post that related to this issue, Exception: You cannot access body after reading from request's data stream
Anyway use request.data instead of request.body in DRF
#api_view(['POST'])
#renderer_classes((JSONRenderer,))
def index(request):
if request.method == 'POST':
results = []
serializer = ScrapeSerializer(data=request.data)
if serializer.is_valid():
url = request.data["url"]
The request.data returns the parsed content of the request body and it will be dict like object, hence the dot operation (request.data.url) won't works here.
To access the request body of a POST request, you could do this by url = request.POST.get("url")

Json parsing django rest framework

I want to parse incoming POST data in django views.py file
POST data:
{
"number" : "17386372",
"data" : ["banana","apple","grapes" ]
}
Here is how I tried to read above incoming data with request
views.py
class Fruits(APIView):
def post(self, request, format=None):
if request.method == "POST":
number = request.data.get('number')
fruits_data = json.loads(request.body)
if number not in [None, '', ' ']:
try:
response = {"return": "OK","data":fruits_data['data']}
return Response(response)
except:
return Response({"return": "NOT OK"})
else:
return Response({"return": "NOT OK"})
else:
return Response({"return": "NOT OK"})
ERROR:
You cannot access body after reading from request's data stream
The Django json parser does this already for you:
from rest_framework import parsers
class Fruits(APIView):
parser_classes = (parsers.JSONParser,)
def post(self, request, format=None):
number = request.data['number']
fruits = request.data['data']
If the Content-Type of your http request is already set properly to application/json you do not even need to specify the parser.
request.data and request.body are the two mechanisms, which reads the raw http request and construct data in a format, that is suitable to be used in python environment. Here the problem is that you are using both of them simultaneously. Thus, the inputstream of http connection is already read, by request.data call. Now request.body also tries to access the same stream, which doesn't contain now any data. Thus, it's throwing an error.
For you, I think following code will work :
fruits_data = json.loads(request.body)
number = fruits_data["number"]

Django Rest Framework custom response message

I have two questions about Django Rest Framework response message
1.
When use generics.ListCreateAPIView or RetrieveDestroyAPIView , usually return a resource
For example ,call /map/ with POST Method
The result will like a object :
{
"x_axis": "23",
"y_axis": "25",
"map_id": 1,
}
I want to know can I edit this message to custom like below?
{"Success":"msg blablabla"}
2.
When I use serializers.ValidationError ,
I can write my custom message
if I use raise serializers.ValidationError("map_id does not exist")
The response message will be
{"map_id":["map_id does not exist"]}
Can I edit this part to custom like below?
{"FAIL":"map_id does not exist"}
I want to know this because front-end don't want this format,
They like :
{"Success":"msg blablabla"}
{"Fail":"msg blablabla"}
{"USERNAME_DUPLICATE":1001}
{"FIELD_REQUIRED":1002}
So they can be more convenient to tell user the operate error cause ?
1 Overwrite the create method on the view and put something like this:
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response({"Success": "msb blablabla"}, status=status.HTTP_201_CREATED, headers=headers)
2 In the code above, change raise_exception to False and return whatever you want if the serializer is not valid. i.e.:
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if not serializer.is_valid(raise_exception=False):
return Response({"Fail": "blablal", status=status.HTTP_400_BAD_REQUEST)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response({"Success": "msb blablabla"}, status=status.HTTP_201_CREATED, headers=headers)
You are using CBV so you'll be able to create your custom generic classes that extends DRF's classes, and DRY
however, I'd say that you shouldn't add "success" or "fail" in your responses... if the http code is 2xx the user will know it was OK, 4xx when the request has a problem and 5xx when there was a error on your code (or the server), you don't need to repeat that information on the body of your response, just use the HTTP status codes
Hope this helps

CSRF verification fails on requests.post()

Can't figure this one out. The CSRF verification works fine in all my Django template views. Here I'm trying to post from a python client using techniques I've found in other posts. The client.get(url) call does provide the token (the debugger shows, for example: client.cookies['csrftoken'] = 'POqMV69MUPzey0nyLmifBglFDfBGDuo9') but requests.post() fails with error 403, CSRF verification failed. What's going on?
My Django view (with some dummy stuff in the methods):
class CameraUpload(View):
template_name = "account/templates/upload.html"
def get(self, request):
dummy = VideoForm()
return render(request, self.template_name, {'form': dummy})
def post(self, request):
dummy = VideoForm()
return render(request, self.template_name, {'form': dummy})
And the client that's trying to do the post:
import requests
url = 'http://127.0.0.1:8000/account/camera_upload/'
client = requests.session()
client.get(url)
csrftoken = client.cookies['csrftoken']
payload = {
'csrfmiddlewaretoken': csrftoken,
'tag': '69'
}
r = requests.post(url, data=payload)
EDIT:
Tried adding the referer as per this link so code now looks like:
r = requests.post(url, data=payload, headers=dict(Referer=url))
but same problem exists.
You should be using your session (client) for the post:
r = client.post(url, data=payload, headers=dict(Referer=url))

Sending post data from angularjs to django as JSON and not as raw content

I have a request like this:
$http({
method: 'POST',
url: '/url/',
data: 'test=data'
})
In my django views:
class SomeClass(View):
def get(self, request):
return HttpResponse("Hello")
def post(self, request):
print request.post
print request.body
return HttpResponse("Done")
So when I do request.POST I get an empty query dict :<QueryDict: {}>
But my request.body has: test=data
So I believe django receives the data as url-encoded parameters and not as a dictionary.
How do I send or receive this data as JSON/Dict ?
When calling ajax, you recieve encoded json string in request body, so you need to decode it using python's json module to get python dict:
json.loads(request.body)
In my case works something like
$http({
url: '/url/',
method: "POST",
data: $.param(params),
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
})
Or more nice variant:
app.config ($httpProvider) ->
...
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
and then
$scope.save_result = $http.post('/url/', $.param(params))
http://www.daveoncode.com/2013/10/17/how-to-make-angularjs-and-django-play-nice-together/
I am using zope2 where I used simplejson to decode the request json into python dictionary as:
request_dict = simplejson.loads(request.get('BODY','')
It's working correctly for me. In this way I am able to use angularjs default json request rather than converting it into form post.
I improved mariodev's solution a bit by creating a decorator:
# Must decode body of angular's JSON post requests
def json_body_decoder(my_func):
def inner_func(request, *args, **kwargs):
body = request.body.decode("utf-8")
request.POST = json.loads(body)
return my_func(request, *args, **kwargs)
return inner_func
#json_body_decoder
def request_handler(request):
# request.POST is a dictionary containing the decoded body of the request
Now I just add the #json_body_decoder decorator whenever I create a request handler that deals with post data in application/json.
For angular 4 and Django Rest Framework use request.data to get json object.
like:
posted_data = request.data
The $http service expects a JS object, not a string. Try this:
$http({
method: 'POST',
url: '/url/',
data: {test: 'data'}
})

Categories

Resources