I am tyring to create a User Registration API, but I am getting an error when using a POST request in the following view:
#action(methods=['GET', 'POST'], detail=False, permission_classes=[AllowAny])
def users(self, request):
if request.method == 'GET':
queryset = User.objects.all()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data, status=status.HTTP_201_CREATED)
elif request.method == 'POST':
queryset = User.objects.all()
serializer = UserSerializer(queryset, many=True)
if serializer.available():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
The exception returned is the following:
Cannot call `.is_valid()` as no `data=` keyword argument was passed when
instantiating the serializer instance.
You pass an object or a queryset to a serializer when you want to serialize it, so it's fine in your GET method.
However, when you want to create an object using a serializer, you have to pass the data parameter before calling .is_valid(), and only then you can create your new objects withsave(). This is because the serializer needs to validate the data given to him before he does any save.
What you should do is the following:
[...]
elif request.method == 'POST':
serializer = UserSerializer(data=request.data, many=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Related
I am converting my function-based views to class-based views. Following the official documentation, I was getting errors using the Response imported from rest_framework. When using HttpResponse it's working fine. The error I am getting with Response is:
.accepted_renderer not set on Response
This is my view:
def get(self, request):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
What could be the reason for this?
serializer:
class Meta:
model = User
fields = '__all__'
instead of this:
return Response(serializer.data)
try this:
return Response(serializer.data, status=status.HTTP_200_OK) #Status is depends on your code
I think your serializer is not saved so that's why you are getting that error
def get(self, request):
users = User.objects.all()
serializer = UserSerializer(users, data=request.data, many=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Final thing try this.
I wonder why Django REST Framework build-in documentation doesn't display methods for a User. I have only available list, create, read, update for these URLs:
url(r'^users$', views.UserList.as_view()),
url(r'^users/(?P<user_id>\w+)$', views.UserDetail.as_view()),
views.py:
#permission_classes([CustomPermission])
class UserList(GenericAPIView):
"""
get: Return all users.
post: Create a user.
"""
serializer_class = UserSerializer
def get(self, request):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
#permission_classes([UserPermission])
class UserDetail(GenericAPIView):
"""
get: Return user by ID.
put: Update user by ID.
delete: Delete user by ID.
"""
serializer_class = UserSerializer
def get(self, request, user_id):
try:
user = User.objects.get(id=user_id)
except User.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = UserSerializer(user)
return Response(serializer.data)
def put(self, request, user_id):
try:
user = User.objects.get(id=user_id)
except User.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = UserSerializer(user, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, user_id):
try:
user = User.objects.get(id=user_id)
except User.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
user.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
However example shown below is not visible in build-in documentation. I have also Swagger documentation in the project and everything is displayed properly.
urls.py:
url(r'^users/(?P<user_id>[0-9]+)/object$', views.UserObject.as_view()),
views.py:
#permission_classes([UserPermission])
class UserObject(GenericAPIView):
"""
post: Create a user object by his ID.
get: Return a user object by his ID.
put: Update a user object by his ID.
delete: Delete a user object by his ID.
"""
serializer_class = ObjectSerializer
def post(self, request, user_id):
try:
Object.objects.get(user=user_id)
return Response(status=status.HTTP_403_FORBIDDEN)
except Object.DoesNotExist:
serializer = ObjectSerializer(data=request.data)
serializer.fields['user'].required = False
if serializer.is_valid():
serializer.save(user_id=user_id)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def get(self, request, user_id):
try:
object = Object.objects.get(user=user_id)
except Object.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = ObjectSerializer(object)
return Response(serializer.data)
def put(self, request, user_id):
try:
object = Object.objects.get(user=user_id)
except Object.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = ObjectSerializer(object, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, user_id):
try:
object = Object.objects.get(user=user_id)
except Object.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
object.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
There should be visible path users/{user_id}/object Any idea why is not?
Edit 2017-08-19
I've made a PR with the fix which has already been merged. So may be try to get latest version.
Edit 2017-08-13
This is a bug with DRF default documentations, where extra actions with more than one method are not displayed in the docs.
Solution: use swagger
Original
I tried reproducing it, looks like there is a bug in the coreapi from django-rest-framework.
I've tried with the doc generator swagger for rest-framework and it looks fine.
There is a bug in the url if you remove object from users/{user_id}/object it works. If you try for example xsers/{user_id}/ it'll work.
You could change the design approach using a ViewSet.
ViewSet provides actions instead of mapping directly to the method. It's another level of abstraction, usually clearer.
class UserViewSet(viewsets.ViewSet):
"""
retrieve:
Return the given user.
list:
Return a list of all the existing users.
create:
Create a new user instance.
update:
Update a user.
"""
serializer_class = UserSerializer
def list(self, request):
# Here you should put the code for GET user/
pass
def create(self, request):
# Here you should put the code for POST user/
pass
def retrieve(self, request, pk=None):
# Here you should put the code for RETRIEVE user/{pk}
pass
def update(self, request, pk=None):
# Here you should put the code for UPDATE user/{pk}
pass
#detail_route(methods=['get'])
def objects(self, request, pk=None):
if request.method == 'GET'
....
And in your urls
from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = router.urls
More info http://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing
I am using Postman + Django rest framework to create a Post request locally but I keep getting a ParseError. My Get requests are working fine but the post requests are not working as expected.
JSON parse error - Expecting ',' delimiter: line 3 column 2 (char 37)
I am not even getting the 400 error defined in the code and Postman returns a 500 internal server error message.
Here is my photo_list views.py:
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from .models import Photo
from .serializers import PhotoSerializer
#csrf_exempt
def photo_list(request, pk=0):
"""
List all photos, or create a new one.
"""
if request.method == 'GET':
if int(pk) > 0: # convert pk to an int then check if it is greater than zero
photo = Photo.objects.get(pk=pk)
serializer = PhotoSerializer(photo, many=False)
return JsonResponse(serializer.data, safe=False)
photos = Photo.objects.all()
serializer = PhotoSerializer(photos, many=True)
return JsonResponse(serializer.data, safe=False)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = PhotoSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)
Since you're using a function based view, you might want to try importing the api_view decorator from DRF and use it to modify photo_list. This is to ensure the view receives an instance of Request and allow it to return a Response. See DRF function based views section here.
from rest_framework.decorators import api_view
...
...
#api_view(['GET', 'POST'])
def photo_list(request)
...
elif request.method == 'POST':
d = request.data
serializer = PhotoSerializer(data=d)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)
your request is being parsed as soon as you call that, and request.DATA is actually returning the dictionary that you were expecting to parse.
json = request.DATA
Please update your code like below
from rest_framework.decorators import api_view
#csrf_exempt
#api_view(['GET', 'POST'])
def photo_list(request, pk=0):
"""
List all photos, or create a new one.
"""
if request.method == 'GET':
# your existing code
elif request.method == 'POST':
serializer = PhotoSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)
The error is raising because you are not accepting the data from the request.POST to serializer.
Edit your view like this,
elif request.method == 'POST':
serializer = PhotoSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)
Edit your view like this,
elif request.method == 'POST':
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
How do add one extra field (non-model field) to serializer.data dict ? I would like to add ("status" : "available") before sending the response. I tried this -
if serializer.is_valid():
serializer.save()
## This one doesn't work as serializer.data is of the type 'ReturnDict'
serializer.data.append(("status","available"))
## OR
serializer.data["status"] = "available"
return Response(serializer.data, status=status.HTTP_201_CREATED)
I also want to modify serializer.data's field names before sending the response. Is there any way to do this?
You can add an extra field to the serializer like this:
class MySerializer(serializers.ModelSerializer)
status = serializers.SerializerMethodField('get_status')
class Meta:
model = MyModel
read_only_fields = ('status',)
def get_status(self, obj):
""" Get the current objects status """
if obj.available == 1:
return 'available'
else:
return 'not available'
One way to change the field names would be to use a middleware class to format the response. I would do this if I would have to format the response for the whole API.
I am trying to POST data to my Django app using Django rest-frameWork...
My view is :
#csrf_view_exempt
class subscriptionsList(APIView):
def post(self, request, format=None):
key = self.request.QUERY_PARAMS.get('appKey', None)
keyData = app.objects.filter(appKey=key).exists()
if keyData == True:
serializer = PostSubscriptionDetailSerializer(data=request.DATA)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
urls.py :
url(r'^subscribe/$',subscriptionsList,name='subscriptions-list'),
serializer.py
class PostSubscriptionDetailSerializer(serializers.ModelSerializer):
class Meta:
model = subscriptions
fields = ('subAppName','subStoreName','subTagName','emailID')
Can someone tell me how to POST data using Django REST-frameWork...