Json parsing django rest framework - python

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"]

Related

Django HttpResponseRedirect wont redirect when message sent

I want my page to reload and show the django messages sent to the page when a POST call is made (by another user).
From api I am calling the method like that:
def create(self, request, pk=None):
json_data = json.loads(request.body)
sample_path = json_data['sample_path']
try:
sample = BloodSample.objects.get(sample_path = sample_path)
if json_data['status'] == 201:
BloodSampleAdmin(sample, BloodSample).display_messages(request, sample)
return Response(json_data['body'], status = status.HTTP_201_CREATED)
and the method in BloodSampleAdmin is:
def display_messages(self, request, sample):
messages.success(request, ("hurray"))
return HttpResponseRedirect(reverse(
"admin:backend_bloodsample_change",
args=[sample.id]
))
I am 100% sure that the method is called (debug). But message wont pop anyway.
I am using Postman to send the POST request.
Any ideas on what is going wrong?

Django: Forbidden (CSRF cookie not set.), Why this is happening , and How to fix it [duplicate]

This question already has answers here:
#csrf_exempt does not work on generic view based class
(4 answers)
Closed 3 years ago.
I wrote a test file to check if the URL works or not and it keeps printing Forbidden (CSRF cookie not set.) could please check what's the problem
#post handler
#csrf_exempt
def post(self, request, *args, **kwargs):
valid_json = is_json(request.body)
if not valid_json:
error_data = json.dumps({'message': 'Invalid data sent, please send using JSON format'})
return self.render_to_response(error_data, status=400)
data = json.loads(request.body)
form = SupervisorForm(data)
if form.is_valid():
obj = form.save(commit=True)
obj_data = obj.serialize()
return self.render_to_response(obj_data, status=201)
if form.errors:
data_error = json.dumps(form.errors)
return self.render_to_response(data_error, status=400)
json_data = json.dumps({'message': 'Not Allowed'})
status_code = HTTP_400_BAD_REQUEST
return self.render_to_response(json_data, status_code)
def post():
data = {
'supervisor_name':'name',
'supervisor_phone': '76786875',
'supervisor_email': 'sdsds#sdsd.com',
'supervisor_image': 'path to local image',
}
json_data = json.dumps(data)
json_loads = json.loads(json_data)
print(type(json_data))
print(type(json_loads))
print(help(requests.put))
r = requests.put('http://127.0.0.1:8000/api', json = json.dumps(data))
return r.json()
You have probably configured your Django to use a CSRF token but have not set it up for your API. Are you able to disable CSRF in your configuration? Otherwise, you'd have to set it up in accordance with the documentation
CSRF is important for websites that are at a high risk of getting hacked through scripts/iframes. CSRF is what prevents your bank account from sending money to a hacker via email/popup scripts. Unless you're building a website that has confidential data scoped to the user (e.g. Facebook, Venmo, PayPal) CSRF is not necessary.

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")

How do I use django rest framework to send a file in response?

I need to send a pdf file and some other parameters in response to a get API call using django rest framework.
How can I do it?
I tried this but it gives an error <django.http.HttpResponse object at 0x7f7d32ffafd0> is not JSON serializable.
#detail_route(methods=['get'])
def fetch_report(self, request, *args, **kwargs):
short_report = open("somePdfFile", 'rb')
response = HttpResponse(FileWrapper(short_report), content_type='application/pdf')
return Response({'detail': 'this works',
'report': response})
The problem here is that you are trying to return a mix of JSON and PDF, which either isn't what you are looking for or is going to return a giant base64-encoded response. PDF is a binary format and JSON is a text format, and you can't really mix them well.
Within a DRF view you can directly return a Django response, which you already appear to be generating (the HttpResponse), and DRF will pass it through and skip the renderers. This is useful in cases like this, as it allows you to return a binary response such as an image or PDF without worrying about DRF's rendering layer causing problems.
#detail_route(methods=['get'])
def fetch_report(self, request, *args, **kwargs):
short_report = open("somePdfFile", 'rb')
response = HttpResponse(FileWrapper(short_report), content_type='application/pdf')
return response
The alternative is to encode the PDF as text, using something like base64 encoding. This will dramatically increase your response sizes, but it will allow you to use DRF's rendering layer without problems.
#detail_route(methods=['get'])
def fetch_report(self, request, *args, **kwargs):
import base64
short_report = open("somePdfFile", 'rb')
report_encoded = base64.b64encode(short_report.read())
return Response({'detail': 'this works',
'report': report_encoded})
But the route I would recommend here is to generate the PDF and store it in either your media storage, or an alternative private location, and provide a direct link to it in your response. This way you don't need to worry about the encoding issues, don't need to directly return the PDF, and don't need to worry about directly serving the PDF.
If you really need to serve binary files directly from your backend (e.g. when they're being generated dynamically) you can use a custom renderer:
from rest_framework.renderers import BaseRenderer
class BinaryFileRenderer(BaseRenderer):
media_type = 'application/octet-stream'
format = None
charset = None
render_style = 'binary'
def render(self, data, media_type=None, renderer_context=None):
return data
Then use it in your action in a viewset:
from rest_framework.decorators import action
#action(detail=True, methods=['get'], renderer_classes=(BinaryFileRenderer,))
def download(self, request, *args, **kwargs):
with open('/path/to/file.pdf', 'rb') as report:
return Response(
report.read(),
headers={'Content-Disposition': 'attachment; filename="file.pdf"'},
content_type='application/pdf')
You can use DRF-PDF project with PDFFileResponse:
from rest_framework import status
from rest_framework.views import APIView
from drf_pdf.response import PDFFileResponse
from drf_pdf.renderer import PDFRenderer
class PDFHandler(APIView):
renderer_classes = (PDFRenderer, )
def get(self, request):
return PDFFileResponse(
file_path='/path/to/file.pdf',
status=status.HTTP_200_OK
)
But, maybe you cannot respond in both formats (json and stream).
You can send pdf file as a response to any request without installing any packages.
You can do the following.
def get(request, *args, **kwargs):
# some code
# some code
with fs.open("path/to/file/Report.pdf") as pdf:
response = HttpResponse(pdf, content_type='application/pdf')
filename = "Report.pdf"
response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
return response
Now, open your postman and hit a get/post request.
Important: Before clicking send button in postman, select the option send and download.
With DRF you can write as following:
pdf_file_in_bytes = AnyModel.file.read() # file is a FileField
pdf_file_name = "PDF's Download Name"
response = Response(
headers={'Content-Disposition': f'attachment; filename={pdf_file_name}'},
content_type='application/pdf'
)
response.content = pdf_file_in_bytes
return response

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