Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django rest framework - using detail_route and detail_list

In my code I have a viewset for the User. I want is to allow only Read operations (/users/42 and /users/) which the ReadOnlyModelViewSet does just fine.

In addition, I want to have a /users/register URL that I can POST to in order to register a new User.

class UserViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @list_route(methods=['post'])
    def register(request):
        serializer = UserSerializer(data=request.DATA)
        if serializer.is_valid():
            user = User.objects.create_user(
                username = serializer.init_data['username'],
                password = serializer.init_data['password'],
            )

            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Couple of questions:

  • Would this be this the right way of doing that?

  • Is there a specific signature for a method if I put it in a list_route or the detail_route decorator? because in the detail_route examples its always the same signature for the method: method_name(self, request, pk=None):

thanks!

like image 265
Ofek Agmon Avatar asked May 23 '15 08:05

Ofek Agmon


2 Answers

detail_route and detail_list will get deprecated on DRF 3.0 instead use @action:

from rest_framework.decorators import action
    @action(methods=['POST'], detail=True)
    def sale(self):
       ...

Use detail=True when this method will account for a single instance of the Model represented by that endpoint and False when it needs to represent a Queryset of that model

like image 95
Gregory Avatar answered Sep 18 '22 17:09

Gregory


Your code is almost correct, you're just missing the right signature on the register method:

def register(self, request):

This is the correct signature according to the documentation. Additionally the tests suggest that it's not possible to pass an additional parameter for routing, and that pk will always be passed for a @detail_route, so you would have to have:

@detail_route
def register(self, request, pk=None):

for detail routes and

@list_route
def register(self, request):

for list routes.

However I would suggest you take advantage of the built in ViewSetMixins as ModelViewSet does internally:

from rest_framework import exceptions, mixins
class UserViewSet(mixins.CreateModelMixin,
               mixins.RetrieveModelMixin,
               mixins.ListModelMixin,
               GenericViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    def create(self, request):
        serializer = UserSerializer(data=request.DATA)
            if serializer.is_valid():
                user = User.objects.create_user(
                    username = serializer.init_data['username'],
                    password = serializer.init_data['password'],
                )

                return Response(serializer.data, status=status.HTTP_201_CREATED)
            else:
                return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

For user sign ups in general you can also take a look at django-registration-restframework which I'm currently making work for my project.

Personally I rely on the ModelViewSet in my projects and make sure only properly authorized users can perform certain actions. To do this you can use model wide permissions or in combination with django guardian object specific permissions.

Especially with a REST API you will eventually come to the point that you'd like certain users to perform actions only on certain objects, without having to micromanage every request. Object level permissions can be of great use here.

like image 43
Sebastian Wozny Avatar answered Sep 18 '22 17:09

Sebastian Wozny