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

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:

Related

Object update by PATCH for Django REST Framework

I am using viewsets.ModelViewSet
from rest_framework import viewsets
class ProjectViewSet(viewsets.ModelViewSet):
serializer_class = s.ProjectSerializer
queryset = m.Project.objects.all()
def patch(self,request,*args,**kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance,data = request.data)
if serializer.is_valid():
self.perform_update(serializer)
return Response(serializer.data)
return Response()
Then I test to update the object via django restframework UI.
Then this error occurs.
My basic idea that changing object data via PATCH is correct?
How can I update the data via Django REST Framework
Expected view ProjectViewSet to be called with a URL keyword argument named "pk". Fix your URL conf, or set the `.lookup_field` attribute on the view correctly.
First, the problem is directly related to URL setup, in any case I would suggest using DRF routers:
from django.urls import path, include
from .views import ProjectViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'projects', ProjectViewSet)
urlpatterns = [
path('api/', include(router.urls)),
]
Secondly, the actions provided by ModelViewSet are .list(), .retrieve(), .create(), .update(), .partial_update(), and .destroy().
The action related to PATCH is partial_update, so:
class ProjectViewSet(ModelViewSet):
serializer_class = ProjectSerializer
queryset = Project.objects.all()
def partial_update(self, request, *args, **kwargs):
partial = True
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response(serializer.data)
Original code of UpdateModelMixin
.partial_update:
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
As you can see it still calls .update which does almost exactly the same:
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
return Response(serializer.data)

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

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.

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

API call to update a field of an object

So I have an model serializer which consists of
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'name', 'description')
This is my ViewSet
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
This is my URLs.py file:
from django.conf.urls import include, url
from rest_framework import routers
import views
router = DefaultRouter()
router.register('user', views.UserViewSet)
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^login/', include('rest_framework.urls', namespace='rest_framework'))
]
Using the serializer, I can make it print out the objects inside my database. If I have the object PK/ID, I want to be able to update the field id or name of the object. Is there a way I can do that with a patch/post request using the serializer? I'm new to this so I'd love it if someone can help me out with this.
I'm thinking of just doing a POST request, then have it do this:
user = User.objects.get(id=id)
user.name = "XXXXX"
user.save()
But I want to do this using the serializer, using a PATCH request.
the below code will help to you,
**filename : views.py**
from user.models import User
from users.serializers import UserSerializer
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class UserList(APIView):
"""
List all users, or create a new user.
"""
def get(self, request, format=None):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
def post(self, request, format=None):
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)
class UserDetail(APIView):
"""
Retrieve, update or delete a user instance.
"""
def get_object(self, pk):
try:
return User.objects.get(pk=pk)
except User.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
user = self.get_object(pk)
serializer = UserSerializer(user)
return Response(serializer.data)
def put(self, request, pk, format=None):
user = self.get_object(pk)
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, pk, format=None):
user = self.get_object(pk)
user.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
**filename : urls.py**
from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from users import views
urlpatterns = [
url(r'^users/$', views.UserList.as_view()),
url(r'^user/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
Reference : http://www.django-rest-framework.org/tutorial/3-class-based-views/
Django rest framework comes with some pre-defined concrete generic views such as UpdateAPIView, RetrieveUpdateAPIView.
First you need to create a view for user which uses one of the views which can update. The update views provides handlers for patch method on the view.
RetrieveUpdateAPIView
Used for read or update endpoints to represent a single model instance.
Provides get, put and patch method handlers.
Now, use this to create a view:
class UserDetail(generics.RetrieveUpdateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
To access this view you need to have a url which uses user's primary key to access the user:
url(r'users/(?P<pk>\d+)/$', UserDetail.as_view(), name='api-user-detail'),
Then using PATCH call you can update the user's name.
Since you're using a ModelViewset, this capability should be built in. If you use the browsable API to navigate to /user/<pk>/, you'll see the operations you can perform on that object. By default, a ModelViewset provides list(), retrieve(), create(), update(), and destroy() capability.
http://www.django-rest-framework.org/api-guide/viewsets/#modelviewset
You can also override any or all of the provided methods, however an update of a single object is built in to DRF ModelViewsets. Use curl to try a PATCH to /user/<pk>/ with the information you'd like to update.

Django-REST: Get User by username instead of primary-key

I am using the basic UserViewSet derived from ModelViewSet.
Retrieving a user with a primary-key via api/users/<pk> works fine.
But I also want to be able to retrieve a User by Username.
I have added a new detail route but I always get 404 on my server when I try to get the user with the url /api/users/retrieve_by_username/altoyr.
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
#detail_route(methods=['get'])
def retrieve_by_username(self, request, username=None):
try:
user = User.objects.get(userName=username)
return Response(user)
except User.DoesNotExist:
return Response("No user with username found!", status=status.HTTP_400_BAD_REQUEST)
The Urls are registered via a router:
router = DefaultRouter()
router.register(r'users', views.UserViewSet)
# The API URLs are now determined automatically by the router.
# Additionally, we include the login URLs for the browsable API.
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
I think I am missing an important part of building rest urls.
You can do this by adding a list route like:
#list_route(methods=['get'], url_path='retrieve_by_username/(?P<username>\w+)')
def getByUsername(self, request, username ):
user = get_object_or_404(User, username=username)
return Response(UserSerializer(user).data, status=status.HTTP_200_OK)
and the url will be like:
/api/users/retrieve_by_username/altoyr
You can override the retrieve() ViewSet action. You'll find more detail here: https://www.django-rest-framework.org/api-guide/viewsets/
from django.shortcuts import get_object_or_404
from rest_framework.response import Response
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
def retrieve(self, request, pk=None):
queryset = User.objects.filter(username=pk)
contact = get_object_or_404(queryset, pk=1)
serializer = ContactSerializer(contact)
return Response(serializer.data)
The answer from Anush Devendra is right but need a little update due to deprecations on v3.9.
action decorator replaces list_route and detail_route
Both list_route and detail_route are now deprecated in favour of the single action decorator. They will be removed entirely in 3.10.
The action decorator takes a boolean detail argument.
Replace detail_route uses with #action(detail=True).
Replace list_route uses with #action(detail=False).
...
from rest_framework.decorators import action
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
from rest_framework import status
class UserViewSet(viewsets.ModelViewSet):
...
#action(methods=['get'], detail=False,
url_path='username/(?P<username>\w+)')
def getByUsername(self, request, username):
user = get_object_or_404(User, username=username)
data = UserSerializer(user, context={'request': request}).data
return Response(data, status=status.HTTP_200_OK)
I add context={'request': request} because I have url as HyperlinkedIdentityField in my serializer. If you don't have it, you probably don't need it.

Categories

Resources