Ilia_Mochalov
Ilia_Mochalov

Reputation: 39

Django REST API custom methods for generic views

I'm intern and work on a project where I develop DRF API that need to interact with mobile app written by my colleague with Ionic framework. We are creating new user. My view method is following:

class NewUser(generics.CreateAPIView):
    model = User
    permission_classes = [permissions.AllowAny]
    serializer_class = NewUserSerializer

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        token, created = Token.objects.get_or_create(user=serializer.instance)
        return Response({'token': token.key}, status=status.HTTP_201_CREATED, headers=headers)

When someone wants to create new user via POST request if user name has't been taken yet, then API return 201 status code and token in JSON, if user name already taken it returns 400 status and error message in JSON. MY colleague request me to change status message to 200 when he tries to create username with name that already exist. He says that he can't consume the ERROR response. His code looks like:

$http.post(url,{
username:$scope.tel,
password:$scope.passwd
}).success(function(data){
alert(data);
$ionicLoading.hide();
console.log(data);
})

Question: 1) Should I tweak my API to send 200 status instead of more logical 400 for 'user already register' error? I tried to change my code, But i couldn't find the method to override in CreateAPIView/ModelSerializer of DRF. I ended up rewriting my view class to method:

@api_view(['POST'])
def newUser(request):
    """
    Saves a new user on the database
    """
    if request.method == 'POST':

        serializer = NewUserSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            token, created = Token.objects.get_or_create(user=serializer.instance)
            return Response({'token': token.key}, status=status.HTTP_201_CREATED, headers=serializer.data)
        else:
            return Response(serializer.errors, status=status.HTTP_200_OK)

Question: 2) If I want to change behaviorof API and responce, which method should I override 3) I'm new to Django and still don't qiute know where we should use generic views VS. @.... methods

Upvotes: 1

Views: 8188

Answers (2)

Serhii Zelenchuk
Serhii Zelenchuk

Reputation: 345

Save and deletion hooks:

The following methods are provided by the mixin classes, and provide easy overriding of the object save or deletion behavior.

perform_create(self, serializer) - Called by CreateModelMixin when saving a new object instance. perform_update(self, serializer) - Called by UpdateModelMixin when saving an existing object instance. perform_destroy(self, instance) - Called by DestroyModelMixin when deleting an object instance.

These hooks are particularly useful for setting attributes that are implicit in the request, but are not part of the request data. For instance, you might set an attribute on the object based on the request user, or based on a URL keyword argument.

https://www.django-rest-framework.org/api-guide/generic-views/#methods

class CourseOrder(generics.CreateAPIView):
    serializer_class = serializers.OrderCoursesSerializer
    permission_classes = [permissions.AllowAny]

    # hook before creating
    def perform_create(self, serializer):
        # print(serializer['name'].value)

        # save post data
        serializer.save()

        try:
            subject, from_email, to = 'New order', '[email protected]', '[email protected]'
            text_content = 'New order'
            html_content = '''
               <p>client name: %s </p>
               <p>client phone: %s </p>    
                ''' 
                % (serializer['name'].value, serializer['mobile'].value)

            msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
            msg.attach_alternative(html_content, "text/html")
            msg.send()
        except Warning:
            print('Huston we have a problems with smtp')

Upvotes: 0

John
John

Reputation: 5206

200 vs 400 in this case is mostly preference. 400 means "Bad Request". This is generally more correct for a incorrectly formatted request, rather than one that doesn't meet some condition.

200 is just as appropriate it conveys the correct information:

Your request was valid, but I didn't create a new record.

As to how to do the override. The shortest path is to override the CreateAPIView.create and change the response code used. You should also avoid repeating the default behavior of CreateAPIView by calling super.

class CreateUserView(generics.CreateAPIView):
    model = User
    permission_classes = [permissions.AllowAny]
    serializer_class = NewUserSerializer

    def create(self, request, *args, **kwargs):
        response = super(CreateUserView, self).create(request, *args, **kwargs)
        token, created = Token.objects.get_or_create(user=serializer.instance)
        response.status = status.HTTP_200_OK
        response.data = {'token': token.key}
        return response

Personally, I would have also crafted my NewUserSerializer to have a token field and handle the token so I didn't have to do that work in the View. It doesn't belong in a View.

Upvotes: 2

Related Questions