Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django rest framework PUT returns 404 instead of creating an object

I want to be able to create or update an object using the same request. The operation should be idempotent.

Sending a PUT request to DRF work as expected if the object exists but if the object doesn't exists I get a 404 instead of creating it.

models.py:

class Btilog(models.Model):
    md5hash = models.CharField(primary_key=True, max_length=32)
    vteip = models.ForeignKey('vte.VTE')
    timestamp = models.DateTimeField(blank=False)
    source = models.TextField()
    code = models.CharField(max_length=10, blank=False)
    msg = models.TextField(blank=False)

api.py:

class BtilogSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Btilog

class BtilogVSet(viewsets.ModelViewSet):
    queryset = models.Btilog.objects.all()
    serializer_class = BtilogSerializer
    permission_classes = (permissions.AllowAny,)

urls.py:

...
router = routers.DefaultRouter()
router.register(r'btilog', api.BtilogVSet)

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

Failing request

http --form PUT http://192.168.10.121:8888/logger/api/btilog/60c6b9e99c43c0bf4d8bc22d671169b1/ vteip='172.25.128.85' 'code'='Test' 'md5hash'='60c6b9e99c43c0bf4d8bc22d671169b1' 'timestamp'='2015-05-31T13:34:01' msg='Test' source='Test'
HTTP/1.0 404 NOT FOUND
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Type: application/json
Date: Mon, 09 Feb 2015 15:16:47 GMT
Server: WSGIServer/0.1 Python/2.7.6
Vary: Accept, Cookie

{
    "detail": "Not found"
    }

As described here: http://restcookbook.com/HTTP%20Methods/put-vs-post/ the correct behaviour of put should be to create the object if it doesn't exists.

The same error occurs using The Browsable API Tool from DRF to make the request. Is the behaviour of DRF also alike? What I'm doing wrong?

like image 734
Maximiliano Padulo Avatar asked Feb 09 '15 15:02

Maximiliano Padulo


People also ask

How do I return a 404 response in Django?

The Http404 exceptionIf you raise Http404 at any point in a view function, Django will catch it and return the standard error page for your application, along with an HTTP error code 404. In order to show customized HTML when Django returns a 404, you can create an HTML template named 404.

How do I create a custom exception in Django REST framework?

Custom exception handlingThe exception handler function should either return a Response object, or return None if the exception cannot be handled. If the handler returns None then the exception will be re-raised and Django will return a standard HTTP 500 'server error' response.

What is renderers in Django REST framework?

The rendering process takes the intermediate representation of template and context, and turns it into the final byte stream that can be served to the client. REST framework includes a number of built in Renderer classes, that allow you to return responses with various media types.

What is HyperlinkedModelSerializer?

HyperlinkedModelSerializer is a layer of abstraction over the default serializer that allows to quickly create a serializer for a model in Django. Django REST Framework is a wrapper over default Django Framework, basically used to create APIs of various kinds.


1 Answers

Well, maybe you should try to overwrite update method inside your modelviewset, which handle the PUT http method:

class BtilogVSet(viewsets.ModelViewSet):
    queryset = models.Btilog.objects.all()
    serializer_class = BtilogSerializer
    permission_classes = (permissions.AllowAny,)

    def update(self, request, *args, **kwargs):
        try:
            instance = Btilog.objects.get(pk=kwargs['pk'])
            serializer = serializers.BtilogSerializer(instance=instance,data=request.data)
            if serializer.is_valid():
                btilog=serializer.save()
                return Response(serializer.data,status=status.HTTP_200_OK)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        except Btilog.DoesNotExist:
            serializer = serializers.BtilogSerializer(data=request.data)
        if serializer.is_valid():
            btilog=serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
like image 188
Matúš Bartko Avatar answered Oct 11 '22 14:10

Matúš Bartko