I'm trying to package my API data into one GET request simply using standard libraries python.
class GetData(APIView):
def get(self, request, *args, **kwargs):
urls = [url_1,
url_2,
url_3,
url_4
]
data_bundle = []
for x in urls:
response = requests.get(x, headers={'Content-Type': 'application/json'}).json()
data_bundle.append(response)
return Response(data_bundle, status=status.HTTP_200_OK)
The return response has to be JSON data, I'm trying to get it to work but it seems like the response data seems to be overiding eachother? How can I properly create a JSON dictionary of dictionaries.
I've tried switching data_bundle to an empty dictionary instead of a list. However that just caused an error saying:
ValueError: dictionary update sequence element #0 has length 8; 2 is required
Is there a simple way to accomplish this that I'm missing?
Thank you for the help.
class GetData(APIView):
def get(self, request, *args, **kwargs):
urls = [url_1,
url_2,
url_3,
url_4
]
data_bundle = []
for x in urls:
response = requests.get(x, headers={'Content-Type': 'application/json'}).json()
data_bundle.append(response)
return Response(data_bundle, status=status.HTTP_200_OK)
maybe this will help dont use return in body of for cycle. And data will not override each other
Related
I have a view that calls the other functions. Now based on different scenarios, the function has to return some error messages to the user. But if use return in that internal function, view always returns a "200 OK" until an exception has been raised. Below is a sample code -
class BranchViewSet(BaseViewSet):
serializer_class = BranchSerializer
def create(self, request, *args, **kwargs):
branch = super().create(request, *args, **kwargs)
createBranchAdmin(branch.data)
response = Response(branch.data, status=status.HTTP_201_CREATED)
return response
def createBranchAdmin(data):
schema = Branch.objects.filter(BranchId=data['BranchId'])[0]
name = schema.contactPerson
phone = schema.phoneNumber
existingUser = User.objects.filter(phone=phone)
if existingUser:
response = Response("User Already Exists!", status=status.HTTP_400_BAD_REQUEST)
return response
If a user already exists, I want to send this 400 Error to the user, but I get 201, even when the user exists. How to send this response to user directly from here?
Edit - I understand the point #iceagebaby is conveying. But this is a simple scenario, I have put forward. Sometimes there are multiple return points in a function, each carrying a different message and error. So, returning None would not help. If I create a new return variable for each and check it in the original view, in that case, the view becomes quite messy and there is no benefit of writing a new function.
You aren't returning the value of createBranchAdmin, so when you return the response it is always coming from
response = Response(branch.data, status=status.HTTP_201_CREATED)
You should be either setting the return value of createBranchAdmin to a variable (returning None if there is no user or the 400 response if there is) then checking if the return of createBranchAdmin is None and returning accordingly or you could just throw an error and call that function in a try catch block and returning with the correct response (although I'm pretty sure this is a bit slower).
i.e. try something like this
class BranchViewSet(BaseViewSet):
serializer_class = BranchSerializer
def create(self, request, *args, **kwargs):
branch = super().create(request, *args, **kwargs)
ret = createBranchAdmin(branch.data)
if ret is not None: return ret
response = Response(branch.data, status=status.HTTP_201_CREATED)
return response
def createBranchAdmin(data):
schema = Branch.objects.filter(BranchId=data['BranchId'])[0]
name = schema.contactPerson
phone = schema.phoneNumber
existingUser = User.objects.filter(phone=phone)
if existingUser:
response = Response("User Already Exists!", status=status.HTTP_400_BAD_REQUEST)
return response
return None
I want to pass a value through the Headers of a get request.
Im trying the below but it doesn't work,
class ListCategoriesView(generics.ListAPIView):
"""
Provides a get method handler.
"""
serializer_class = CategorySerializer
def get(self, request, *args, **kwargs):
token = request.data.get("token", "")
if not token:
"""
do some action here
"""
if not UserAccess.objects.filter(accessToken=token).exists():
"""
do some action here
"""
else:
"""
do some action here
"""
I want to pass the token in the headers like that :
can anyone help me with this issue,
thanks a lot in advance.
You said it yourself, you're passing it in the headers, so you need to get it from there. DRF does not do anything special to access the headers, so it proxies to the underlying Django HttpRequest object, which makes them available via the META attribute, converted to uppercase and prefixed by HTTP_:
token = request.META.get("HTTP_TOKEN", "")
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"]
Hey guys I'm trying to ingest some data from a third party API when a DRF endpoint is called.
I can easily build the get request using urllib2 and deserialize the json, but is there some way to use django serializers and do it the drf way? Or am I just overthinking this?
import json, urllib2
class IngestThirdPartyView(APIView):
def post(self, request):
data = request.data
url = 'https://third_party.com/media/{}'.format(data['item_id]')
response = urllib.urlopen(url)
# Handle item somehow
item = json.load(response)
Any suggestions for a more DRF friendly way to handle this are very much welcome!
Cheers
From your example code,
import requests
class IngestThirdPartyView():
def post(self, request):
data = request.data
url = 'https://third_party.com/media/{}'.format(data['item_id]')
item = requests.get(url).json() # this will retrieve json response
# then you could deserialize the above json and validate it or something
# as described here in docs http://www.django-rest-framework.org/api-guide/serializers/#deserializing-objects
# Handle item somehow
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