drf-writable-nested icon indicating copy to clipboard operation
drf-writable-nested copied to clipboard

Duplicate child objects on PUT request

Open zero-master opened this issue 8 years ago • 4 comments

Duplicate child objects on PUT request

Hey! Sorry, if it's already covered.

I am working on a non profit project and i've 3 levels of deeply nested models. Objects are created successfully when I use a POST request with below JSON data:

{
	"id": "4c0e3383-aa2d-4ad9-8dc4-1a40bd910ca0",
	"condition_block": {
		"id": "343feaef-23c1-456b-b6e2-d955a86fde9b",
		"referrer": {
			"id": "4de8a35e-b00f-4e64-af0b-9a03f67dfbc1",
			"list": [
				"Google None Profit"
			],
			"kind": "CONTAINS"
		},
		"name": "Google Non Profit Works"
	}
}

But when I issue a PUT request to /filters/4c0e3383-aa2d-4ad9-8dc4-1a40bd910ca0 with this JSON data:

{
	"condition_block": {
		"referrer": {
			"list": [
				"Google Non Profit"
			],
			"kind": "CONTAINS"
		}
	},
	"name": "Google Non Profit Works"
}

It get this error:

IntegrityError at /api/v1/filters/4c0e3383-aa2d-4ad9-8dc4-1a40bd910ca0
duplicate key value violates unique constraint "api_conditionblock_filter_id_key"
DETAIL:  Key (filter_id)=(4c0e3383-aa2d-4ad9-8dc4-1a40bd910ca0) already exists.

It just seems to create new child objects with the SAME parent id and fails.

Is this how it is designed to work?

Desired behavior

Fetch every child objects by ID and update them, especially when it's already known that they are in One to One relationship.

Models

class ReferrerCondition(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=200)
    condition_block = models.OneToOneField(
        ConditionBlock, null=True, related_name='referrer')


class ConditionBlock(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=200)
    filter = models.OneToOneField(
        Filter, null=True, related_name='condition_block')


class Filter(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=200)

Serializers


class ReferrerConditionSerializer(EnumFieldSerializerMixin,
                                  serializers.ModelSerializer):
    class Meta:
        model = ReferrerCondition
        exclude = ('condition_block', )


class ConditionBlockSerializer(WritableNestedModelSerializer):

    referrer = ReferrerConditionSerializer()

    class Meta:
        model = ConditionBlock
        exclude = ('filter', )


class FilterSerializer(EnumFieldSerializerMixin,
                       WritableNestedModelSerializer):

    condition_block = ConditionBlockSerializer()

    class Meta:
        model = Filter
        exclude = ()

Views

class FilterViewSet(viewsets.ModelViewSet):
    queryset = Filter.objects.all()
    serializer_class = FilterSerializer

zero-master avatar Nov 05 '17 20:11 zero-master

Appears to be caused by https://github.com/beda-software/drf-writable-nested/issues/28

cjroth avatar Nov 13 '17 22:11 cjroth

I believe this PR will fix it: https://github.com/beda-software/drf-writable-nested/pull/29

cjroth avatar Nov 16 '17 22:11 cjroth

Hello @zero-master! The latest version 4.0.2 contains the fix of the problem with OneToOne updating without specifying id. I will appreciate if you check your code with the latest version of the package and leave feedback if everything is OK.

ruscoder avatar Apr 20 '18 17:04 ruscoder

Hi, I have nested reverse relation (fk) which I try to update (there is a unique constraint on pk and additional field on in nested model). When I try to update it I've got IntegrityError: duplicate key value violates unique constraint. Seems that it try to create nested object instead of updating it.

However , it works when I pass directly 'id' for the nested object that is supposed to be updated in request: works: "test_phase": [{"id": 4 ,"phase": "24h" , "result": "passed"}]
doeasn't: "test_phase": [{"phase": "24h" , "result": "passed"}]

mszpulak avatar Aug 08 '18 08:08 mszpulak