[Solved]-How to support all REST operations for an endpoint in django rest framework

7👍

The easiest way is to do it this way.

keep the models class the same

views.py

from rest_framework import viewsets
#impost serializer and model class for subscription

class SubscriptionViewSet(viewsets.ModelViewSet):

    serializer_class = SubscriptionSerializer

    def get_queryset(self):
        queryset = Subscription.objects.all()
        #if you need to get subscription by name
        name = self.request.QUERY_PARAMS.get('name', None)
        if name is not None:
            queryset = queryset.filter(name=name)

        return queryset 

serializers.py

 class SubscriptionSerializer(serializers.ModelSerializer):
     class Meta:
         model = Subscription
         fields = ('name','quantity', 'stripe_id')

 # django will handle get, delete,patch, update for you ....
 # for customization you can use def update or def create ... to do whatever you need
 # def create(self, validated_data):
 # you can handle the email here
 # and something like subscription=        Subscription (name=validated_data['name'],vendor=validated_data['quantity']...)
 # subscription.save()
 # it will save whatever you want

urls.py

#use the router to handle everything for you
from django.conf.urls import patterns, include, url
from rest_framework import routers
#import your classes 


router = routers.DefaultRouter()
router.register(r'subscription', views.SubscriptionViewSet,base_name='subscription')

urlpatterns = patterns('',
url(r'^', include(router.urls)),

)
👤ETS

3👍

For the creation of an Object you must implement the create function as described in the official documentation, found here. For patching you could use the partial argument from within you view class:

SubscriptionSerializer(subscription, data={'something': u'another', partial=True)

For deletion of the a Subscription, that could be done when you get the delete call as so in your view class:

if request.METHOD == 'DELETE':
       subscription = Subscription.objects.get(pk=pk)
       subscription.delete()

See this tutorial for complete example

Further more I think that you should include the “id” field in the SubscriptionSerialiser Meta class, otherwise it will be difficult to do the updates/deletions. I hope this helped a little.

Cheers,
Tobbe

3👍

  1. When you want to use a method that allow make these operations you have to use a @detail_route() where you can say as well which methods will you use, like in the docs is said:

    @detail_route(methods=[‘post’])
    def set_password(self, request, pk=None):
    user = self.get_object()
    serializer = PasswordSerializer(data=request.data)

So to be able to use them you should add the next decorator

@detail_route(methods=['post', 'patch'])

  1. To add another parameters you can do it for the .save() parameter. You just have to indicate the name of this and them just override your .save() model to check if that email belongs or not to the user that is trying to do the subscription. Here I paste you what the Django Rest docs says:

” Passing additional attributes to .save()

You can do so by including additional keyword arguments when calling .save(). For example:

serializer.save(owner=request.user)

Here I leave you the link for more information:

http://www.django-rest-framework.org/api-guide/serializers/#passing-additional-attributes-to-save

3👍

  1. Using the above when I send a PATCH request, I get an error. How can I fix this?

Expected view SubscriptionDetail to be called with a URL keyword
argument named “pk”. Fix your URL conf, or set the .lookup_field
attribute on the view correctly.

The error is caused because unlike create request, patch/update require a pk to know which object to update. That is why you have to supply the pk value for it. So, your url for PUT, DELETE andPATCH must have at least named parameter like this –

subscription = SubscriptionDetail.as_view({
     'patch': 'update'
 })
url(r'^rest-auth/subscription/(?<pk>(\d+))$', subscription, name='something'),

an example url will be – rest-auth/subscription/10 where 10 is the pk or id of the object. Django Rest Framework will then load the object internally to be updated.

  1. While sending the patch request I would also like to send an ’email’ field which is not on the subscription model. Is this possible to do? I need the email field in the POST (create) operation so that I know which user the subscription belongs to.

To add custom parameters, first declare the property in serializer, it is better to keep it required=False, so that other request does not throw error –

class SubscriptionSerializer(serializers.ModelSerializer):
    custom_field = serialiers.BooleanField(required=False)

    class Meta:
        model = Subscription
        fields = ('name','quantity', 'stripe_id')

    def update(self, instance, validated_data):
        print "In update"

so far this is enough for the django rest framework to accept the field custom_field and you will find the value in update method. To get the value pop it from the attributes supplied by the framework like this –

    def update(self, instance, validated_data):
        custom_field = validated_data.pop('custom_field', None)
        if custom_field is not None:
            # do whatever you like with the field
        return super().update(instance, validated_data)
        # for python < 3.0 super(SubscriptionSerializer, self).update(instance, validated_data)

2👍

  1. When you overrided (I don’t know if that’s the proper conjugation of overriding a method) the update method, you stopped the ability to PUT or PATCH and object. Your new method only prints out “In update” but doesn’t save the instance. Look at the update method from the serializer.ModelSerializer object:

    def update(self, instance, validated_data):
        raise_errors_on_nested_writes('update', self, validated_data)
    
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance
    

    Notice the last few lines where the instance is saved with the values and then returned. Remove your update method on the SubscriptionSerializer object. This let’s your parent object’s create, update, retrieve, and delete methods do their magic which supports PATCH and PUT updates. The next problem is that your urls.py is using the Django rather than the REST framework router. Change it to this:

    from rest_framework.routers import DefaultRouter
    router = DefaultRouter()
    router.register(r'subscription', SubscriptionDetail)  
    

That should solve the patch update problem.

  1. I don’t think you can add an email field in your patch method without the attribute on the subscription model. That’s just a guess on my part, and I may be wrong. Does the email field map to anything on any object? Can you use a ForeignKey to map it?

I hope that works for you, good luck!

0👍

In view.py you just need set the class with:

class SubscriptionDetail(mixins.CreateModelMixin,
        mixins.ListModelMixin,
        mixins.RetrieveModelMixin,
        mixins.UpdateModelMixin,
        generics.GenericAPIView):

and add this to fix .lookup_field :

    def update(self, request, *args, **kwargs):
        log.error("OBJ update kwargs= %s , data = %s" % (kwargs,  str(request.data)))
        pk = request.data.get('id')
        if (kwargs.get('pk') is not None):
            kwargs['pk'] = request.data.get('id')
        self.kwargs['pk'] = request.data.get('id')
        return super().update(request, *args, **kwargs)

and add support to methods do you want :

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

#    def get(self, request, *args, **kwargs):
#        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

#    def patch(self, request, *args, **kwargs):
#        return self.partial_update(request, *args, **kwargs)
#
#    def delete(self, request, *args, **kwargs):
#        return self.destroy(request, *args, **kwargs)

only tweak that remains is get for list or get for retrieve on element but should be easy now add something if we have one pk we may call self.retrieve else we may call self.list

Leave a comment