I'm trying to Post, from Postman, multiple files to my django app. I'm not using Forms, and there isn't a UI aspect to my app. Here is a my view class.
class FileUploader(APIView):
'''
Rest API for FileUploader
'''
permission_classes = (AllowAny,)
parser_classes = (MultiPartParser, )
#csrf_exempt
def post(self, request):
retval = Response(request.data, status=status.HTTP_201_CREATED)
logger.info('New post with the following data: {}'.format(request.data))
With this it says, "TypeError: init() missing 3 required positional arguments: 'META', 'input_data', and 'upload_handlers'"
If I use FormView, my Post has three keys, two represent files, the last is a string. During debugging my request has no field Data, and FILES is empty, and the POST doesn't have any information. Any pointers would be appreciated. I can upload more if that helps.
It's not a duplicate because he was able to upload multiple files and mine doesn't upload any files. I'm struggling to figure out how to find the files within the request and since they aren't there how to set up the views (and not the serialize) to receive multiple files.
enter image description here
Write a view class as
from rest_framework.views import APIView
from rest_framework.response import Response
class FileUploader(APIView):
'''
Rest API for FileUploader
'''
permission_classes = (AllowAny,)
def post(self, request, *args, **kwargs):
files_list = request.FILES
data = request.data
return Response(data={"files": "{} files uploaded".format(len(files_list)),
"data": "{} data included".format(len(data))})
and send it using form-data in POSTMAN
change the above code to like below and include header 'Content-Type': 'multipart/form-data' in the request.
class FileUploader(APIView):
'''
Rest API for FileUploader
'''
permission_classes = (AllowAny,)
parser_classes = (MultiPartParser, )
#csrf_exempt
def post(self, request, *args, **kwargs):
print(request.data)
return Response({"message": "success"})
Related
I am using ViewSets for Profile model but if I send request in Postman I am getting following error.
Unsupported media type \"application/x-www-form-urlencoded\" in request
But I do not have a idea What I am doing wrong.
class ProfileView(viewsets.ModelViewSet):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
parser_classes = (MultiPartParser,)
permission_classes = (IsOwnerOrAdmin,)
def get_queryset(self):
return super(ProfileView, self).get_queryset().filter(user=self.request.user)
def get_object(self):
qs = Profile.objects.filter(user=self.request.user).first()
return qs
def put(self, request):
file = request.data['file']
return Response(status=204)
I have configured in settings.py file as well. But I cannot work this out. Any help would be appericated. Thanks in advance
You specified the MultiPartParser as parser so you need to do a proper multipart query. You can do it by selecting "form-data" option in Postman:
multipart/form-data is the default encoding a web form uses to transfer data. This simulates filling a form on a website, and submitting it. The form-data editor lets you set key-value pairs (using the data editor for your data.) It also lets you specify the content type for each part of a multi-part form request individually. You can attach files to a key as well.
If you don't upload any image, then just change the parser class to FormParser
from rest_framework.parsers import FormParser
class ProfileView(viewsets.ModelViewSet):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
parser_classes = (FormPartParser,)
permission_classes = (IsOwnerOrAdmin,)
I am not able to find any support for making a schema for the file upload API.
The Swagger UI must have a button allowing a tester to upload a file for testing purposes. I am using firebase as a database so serializers and models don't come into the picture. I am using only Django's rest framework.
I have looked at drf-yasg's documentation that suggests using Operation for file upload. But It is a very abstract and obscure documentation.
Make sure you specify the parser_classes in your view. By Default it's JSON parser which doesn't handle file uploads. Use either MultiPartParser or FileUploadParser
class MyUploadView(CreateAPIView):
parser_classes = (MultiPartParser,)
...
#swagger_auto_schema(operation_description='Upload file...',)
#action(detail=False, methods=['post'])
def post(self, request, **kwargs):
# Code to handle file
Check out this issue. You can find how to use #swagger_auto_schema to create something like this
Here is working example from my project
from rest_framework import parsers, renderers, serializers, status
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
class ContactSerializer(serializers.Serializer):
resume = serializers.FileField()
class ContactView(GenericAPIView):
throttle_classes = ()
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.FileUploadParser)
renderer_classes = (renderers.JSONRenderer,)
serializer_class = ContactSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid(raise_exception=True):
data = serializer.validated_data
resume = data["resume"]
# resume.name - file name
# resume.read() - file contens
return Response({"success": "True"})
return Response({'success': "False"}, status=status.HTTP_400_BAD_REQUEST)
I am creating a simple rest api using django REST framework. I have successfully got the response by sending GET request to the api but since I want to send POST request, the django rest framework doesn't allow POST request by default.
As in image(below) only GET,HEAD, OPTIONS are allowed but not the POST request
The GET and POST methods inside of views.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from profiles_api import serializers
from rest_framework import status
# Create your views here.
class HelloApiView(APIView):
"""Test APIView"""
#Here we are telling django that the serializer class for this apiViewClass is serializer.HelloSerializer class
serializer_class = serializers.HelloSerializer
def get(self, request, format=None):
"""Retruns a list of APIViews features."""
an_apiview = [
'Uses HTTP methods as fucntion (get, post, patch, put, delete)',
'It is similar to a traditional Django view',
'Gives you the most of the control over your logic',
'Is mapped manually to URLs'
]
#The response must be as dictionary which will be shown in json as response
return Response({'message': 'Hello!', 'an_apiview': an_apiview})
def post(self,request):
"""Create a hello message with our name"""
serializer = serializer.HelloSerializer(data=request.data)
if serializer.is_valid():
name = serializer.data.get('name')
message = 'Hello! {0}'.format(name)
return Response({'message':message})
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
How to allow POST requests in django REST framework?
The problem with the code was, you have added the def post() after the return statement.
To solve, just correct your indentation level as below,
class HelloApiView(APIView):
def get(self, request, format=None):
return Response()
def post(self, request):
return Response()
without using serializer_class and queryset properties inside viewclass(which inherits viewsets.ModelViewSet) ;
i want to make a get request to a another url and return its response as the result of django get request.
can we do that?
Thanks.
You can put any dummy model and make it work.
class SampleViewSet(APIView):
model = DummyModel
renderer_classes = (JSONRenderer,)
def get(self, request, *args, **kwargs):
if request.method == "GET":
// do some operation
return Response(<json response>, status=status.HTTP_200_OK)
I've got a Django Rest Framework ModelViewSet and am trying to use the TemplateHTMLRenderer to display HTML. Following along in the tutorial:
from rest_framework import permissions, renderers, viewsets
from rest_framework.decorators import link
from . import models, serializers
from .permissions import IsOwnerOrReadOnly
class SnippetViewSet(viewsets.ModelViewSet):
template_name = 'snippet-list.html'
queryset = models.Snippet.objects.all()
serializer_class = serializers.SnippetSerializer
renderer_classes = (renderers.TemplateHTMLRenderer,)
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)
#link(renderer_classes=[renderers.StaticHTMLRenderer])
def highlight(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
def pre_save(self, obj):
obj.owner = self.request.user
If I add a key in def resolve_context() I can access the model objects in my template that are passed into the RequestContext. If I don't add the data key then I don't know how to access the Snippets.
def resolve_context(self, data, request, response):
if response.exception:
data['status_code'] = response.status_code
#return RequestContext(request, data) # original source on github
return RequestContext(request, {'data': data}) # if I add a key I can access it
So I've got to be missing something easy or how I'm expecting this to behave is not how the authors intended?
I would go this way:
class SnippetViewSet(viewsets.ModelViewSet):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
renderer_classes = (renderers.JSONRenderer, renderers.TemplateHTMLRenderer)
def list(self, request, *args, **kwargs):
response = super(SnippetViewSet, self).list(request, *args, **kwargs)
if request.accepted_renderer.format == 'html':
return Response({'data': response.data}, template_name='home.html')
return response
and use http://127.0.0.1:8000/snippets/.html to get table (or whatever suffix you use).
This way you don't override resolver for each render type.
Other solution would be to just create dedicated view for list action and only use HTML renderer. But then you would have a small code duplication.
I also met the same question with you, and I also thought so. I came here by Google. I didn't like override "def list(self, request, *args, **kwargs):", because I felt it broke the viewset design idea. After I researched the snippet tutorial and source code in the "site-packages\rest_framework", I got the key, not viewset but "serializer.data". In the "site-packages\rest_framework\serializers.py", I found the class BaseSerializer, i.e., the top base class of ModelSerializer. Its property "data" is defined as follows:
#property
def data(self):
... # omitted the function body here, because it didn't care about this solution.
return self._data
This property data is just the "serializer.data" that is just the response passed to template. So I just overrided the data property in "snippets/serializers.py", and after calling the father's method, set the key for the returned data:
class SnippetSerializer(serializers.ModelSerializer):
#property
def data(self):
return { 'data' : super(serializers.ModelSerializer, self).data } #'data' can be replaced with other wanted name.
class Meta:
model = Snippet
fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
OK, use the name 'data' in your template.
I subclassed and overrode the method that provides the template context, so that the serializer data is available under data within the template context:
from rest_framework.renderers import TemplateHTMLRenderer
class MyHTMLRenderer(TemplateHTMLRenderer):
def get_template_context(self, data, renderer_context):
context = {'data': data}
response = renderer_context['response']
if response.exception:
data['status_code'] = response.status_code
return context
Inside the viewset use renderer class
renderer_classes = (renderers.JSONRenderer, renderers.TemplateHTMLRenderer)
like above and override the ListModelMixin's list method.
mariodev's answer gives the best example also.