DRF Response isn't working with class based views in Django - python

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.

Related

Getting error in .is_valid() method in DRF

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)

"detail": "Method \"GET\" not allowed." Django Rest Framework

I know this question maybe a duplicate, but I have tried many solutions and could not understand any. I have followed this tutorial exactly and yet I get this error on the 'userlist' page. Everything else works just fine. Can somebody point out what the error is ?
class UserList(APIView):
"""
Create a new user. It's called 'UserList' because normally we'd have a get
method here too, for retrieving a list of all User objects.
"""
permission_classes = (permissions.AllowAny,)
http_method_names = ['get', 'head']
def post (self, request, format=None):
self.http_method_names.append("GET")
serializer = UserSerializerWithToken(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)
EDIT:
urls.py
from django.urls import include, path
from classroom.views.classroom import current_user, UserList
from .views import classroom, suppliers, teachers
urlpatterns = [path('', classroom.home, name='home'),
path('current_user/', current_user),
path('users/', UserList.as_view()),
Edit:
Still getting this error,
You need to add GET endpoint url to your urls.py in order to use GET requests. GET url is missing in your urls.py, simply edit your urls.py like:
# urls.py
from django.urls import include, path
from classroom.views.classroom import current_user, UserList
from .views import classroom, suppliers, teachers
urlpatterns = [
path('', classroom.home, name='home'),
path('current_user/', current_user),
path('users/', UserList.as_view()),
path('users/<int:pk>/', UserList.as_view()),
]
And you need to implement get method in yourUserList view such as:
# views.py
class UserList(APIView):
"""
Create a new user. It's called 'UserList' because normally we'd have a get
method here too, for retrieving a list of all User objects.
"""
permission_classes = (permissions.AllowAny,)
http_method_names = ['get', 'head']
def get(self, request, format=None):
users = User.objects.all()
serializer = UserSerializerWithToken(users, many=True)
return Response(serializer.data)
def post(self, request, format=None):
self.http_method_names.append("GET")
serializer = UserSerializerWithToken(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)
Basically the problem is that, there is not functionality defined for GET requests in your view. So either you can add it, like this:
class UserList(APIView):
permission_classes = (permissions.AllowAny,)
http_method_names = ['get', 'head', 'post']
def get(self, request, *args, **kwargs):
serializer = UserSerializerWithToken(User.objects.all(), many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def post (self, request, format=None):
self.http_method_names.append("GET")
serializer = UserSerializerWithToken(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)
Or you can subclass UserList View from ListAPIView.
FYI, permission_classes won't work with APIView. You need to use GenericAPIView or any other generic views to have those functionalities.
I had the same issue but I noticed that in my views.py, I had
renderer_class = api_settings.DEFAULT_RENDERER_CLASSES
I corrected it to :
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
and it worked for me.
maybe you are calling 'post' from where ever you are calling, example as 'postman'
but your function is
def get:
so call as 'get' or change your function to
def post:

Multiple nested resources with multiple methods are not displayed in Django Rest Framework Documentation

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

Django REST Framework ModelSerializer - Object not saved

I've a model, and one of it's field refers to an overrided User instance (changed in Django settings).
When I'm performing a POST from my client, the route ends up here at the create method:
class CatView(ModelViewSet):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
serializer_class = CatListSerializer
def get_queryset(self):
return Cat.objects.filter(owner=self.request.user).order_by('id')
'''
def list(self, request, format=None):
serializer = CatGetSerializer(Cat.objects.filter(owner=request.user), context={'request': request}, many=True)
return Response(serializer.data)
'''
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
def create(self, request, *args, **kwargs):
serializer = CatPutSerializer(data=request.data)
if serializer.is_valid():
serializer.create(serializer.data)
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
When using a PUT to do a partial update on my model, it works fine. But creating one just doesn't work. I manually inject the user instance into the serializer and asks it to create the object. Then... nothing. No exception raises, it returns the proper data, but the object is not in my database, not being saved.
What's the issue here?
EDIT:
When I'm adding the owner field to the CatPutSerializer, it opens security issues since I don't know how to prevent this to be changed as I don't want the client to send me which user to assign. And when I'm duplicating the serializer to be used on POST only requests, it says it misses the owner field...
Here's the CatPutSerializer:
class CatPutSerializer(serializers.ModelSerializer):
class Meta:
model = Cat
fields = ('name', 'weight', 'sterilized', 'image', 'tag', 'dob', 'race', 'gender')
UPDATE:
As suggested, I'm now doing as follows :
def create(self, request, *args, **kwargs):
pdb.set_trace()
serializer = CatPutSerializer(data=request.data)
if serializer.is_valid():
serializer.save(owner=self.request.user)
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
Though removed the perform_create overriding.
SOLUTION:
After further investigation, it doesn't seem related to drf but to Django / PostgreSQL itself, so I checked to Django model save method, and it seems that my custom image processing prevented from new objects to be created... Changed it and now works.
You seem to be overriding both create and perform_create. If you look at the code for CreateModelMixin which the ModelViewSet inherits from you will notice that create calls perform_create, which calls serializer.save(). You don't call perform_create in your create method; you seem to be calling serializer.create(...). If you are overriding create, simply do this:
def create(self, request, *args, **kwargs):
serializer = CatPutSerializer(data=request.data)
if serializer.is_valid():
serializer.save(owner=self.request.user)
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)

How to insert POST data into Django model using rest-frameWork

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...

Categories

Resources