django-rest-framework-bulk icon indicating copy to clipboard operation
django-rest-framework-bulk copied to clipboard

PUT/PATCH support is broken

Open JDougherty opened this issue 8 years ago • 3 comments

I've tried creating a very simple API endpoint following the README:

from django.db import models

from rest_framework import serializers
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin, ListBulkCreateUpdateAPIView


class Foo(models.Model):
    name = models.CharField(max_length=20)
    class Meta:
        app_label = "foobar"


class FooSerializer(BulkSerializerMixin, serializers.ModelSerializer):
    class Meta(object):
        model = Foo
        list_serializer_class = BulkListSerializer

class FooView(ListBulkCreateUpdateAPIView):
    queryset = Foo.objects.all()
    serializer_class = FooSerializer

I've configured a URL for FooView and am able to load the built in rest_framework html page for this API endpoint. I've also created a few instances of the Foo model for testing.

However, when I attempt to make an HTTP PUT/PATCH request to this endpoint to update one of the existing instances, I get a successful return code but no update actually occurs.

curl 'http://127.0.0.1:8000/foo/' -X PUT -H 'Cookie: csrftoken=M8jkbJtUKwjKB7zya8JYvxqJVxkRaPgG' -H 'X-CSRFToken: M8jkbJtUKwjKB7zya8JYvxqJVxkRaPgG' --data 'id=1&name=D' --compressed

The response is simply an empty list [] and the instance with id=1 retains its old name.

I've created a sample project to reproduce this, and would really appreciate any help. I am unable to determine if this is a bug in DRF, DRF-bulk, or a misunderstanding on my part.

[Update] I tried testing a POST request, like so, and it did create a new object:

curl 'http://127.0.0.1:8000/foo/' -X POST -H 'Cookie: csrftoken=M8jkbJtUKwjKB7zya8JYvxqJVxkRaPgG' -H 'X-CSRFToken: M8jkbJtUKwjKB7zya8JYvxqJVxkRaPgG' --data 'name=D' --compressed

The response for this request is: {"id":4,"name":"D"}

However, trying to create a new object with PUT does not work:

curl 'http://127.0.0.1:8000/foo/' -X PUT -H 'Cookie: csrftoken=M8jkbJtUKwjKB7zya8JYvxqJVxkRaPgG' -H 'X-CSRFToken: M8jkbJtUKwjKB7zya8JYvxqJVxkRaPgG' --data 'name=E'

The response for this is: []

I believe that PUT/PATCH support for django_rest_framework-bulk is broken at this point. Python package info is:

Django==1.8
djangorestframework==3.4.4
djangorestframework-bulk==0.2.1

JDougherty avatar Aug 17 '16 23:08 JDougherty

maybe also caused by the confusing readme, like #55?

melinath avatar Oct 25 '16 03:10 melinath

Edit: my problem was unrelated. DRF-bulk doesn't seem to work with a hyperlinked API out of the box, this was the cause. Original post quoted below.

Did you gents get anywhere with this?

I'm knee-deep debugging a problem with the same (?) symptoms. I see if the validated data handed to the BulkListSerializer doesn't contain the pk field, then an empty validation error is raised, eventually appearing in the response as a list with an empty string: ['']

# rest_framework_bulk/drf3/serializers.py:47
if not all((bool(i) and not inspect.isclass(i)
            for i in all_validated_data_by_id.keys())):
    raise ValidationError('')

In my situation, that is the case; during PATCH the pk field (id for me) is not present. I know DRF-bulk takes special care to deal with that situation:

[...] To deal with that, you must use BulkSerializerMixin mixin in your serializer class which will add the model primary key field back to the validated_data.

I will be looking into what might be going wrong with that mixin.

bennullgraham avatar Dec 13 '16 22:12 bennullgraham

The data for each instance that should be updated must be a separate json dict in the payload. Even if you apply a filter to the queryset, only the instances that are explicitly identified in the payload receive the update. I worked this out for my use case of bulk updating all id-filtered resources with the same value as follows:

PATCH http://api.example.com/resource/?id__in=0,1,2,3,4 HTTP/1.1
Content-Type: 'application/json'
# prepare a dict payload for each object
data = [{
    'id': id__,
    'value': 10000     
} for id__ in id_filter]

However, an empty ValidationError is not sufficiently expressive for the underlying problem, please fix that.

pkainz avatar Feb 02 '17 18:02 pkainz