From 4c7e30eca579d32eb4092b09c760483eb0d6ca3a Mon Sep 17 00:00:00 2001 From: Tom Hacohen Date: Tue, 19 May 2020 13:00:54 +0300 Subject: [PATCH] CollectionItem: implement both update and create. --- django_etesync/serializers.py | 37 +++++++++++++++-------------------- django_etesync/views.py | 4 +++- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/django_etesync/serializers.py b/django_etesync/serializers.py index 3f7c28c..5fd6ea8 100644 --- a/django_etesync/serializers.py +++ b/django_etesync/serializers.py @@ -123,36 +123,31 @@ class CollectionItemSerializer(serializers.ModelSerializer): """Function that's called when this serializer creates an item""" stoken = validated_data.pop('stoken') revision_data = validated_data.pop('content') - instance = self.__class__.Meta.model(**validated_data) + uid = validated_data.pop('uid') + + Model = self.__class__.Meta.model with transaction.atomic(): - if stoken is not None: - raise serializers.ValidationError('Stoken is not None') + instance, created = Model.objects.get_or_create(uid=uid, defaults=validated_data) + cur_stoken = instance.stoken if not created else None - instance.save() + if cur_stoken != stoken: + raise serializers.ValidationError('Wrong stoken. Expected {} got {}'.format(cur_stoken, stoken)) + + if not created: + # We don't have to use select_for_update here because the unique constraint on current guards against + # the race condition. But it's a good idea because it'll lock and wait rather than fail. + current_revision = instance.revisions.filter(current=True).select_for_update().first() + current_revision.current = None + current_revision.save() process_revisions_for_item(instance, revision_data) return instance def update(self, instance, validated_data): - """Function that's called when this serializer is meant to update an item""" - stoken = validated_data.pop('stoken') - revision_data = validated_data.pop('content') - - with transaction.atomic(): - if stoken != instance.stoken: - raise serializers.ValidationError('Wrong stoken. Expected {} got {}'.format(instance.stoken, stoken)) - - # We don't have to use select_for_update here because the unique constraint on current guards against - # the race condition. But it's a good idea because it'll lock and wait rather than fail. - current_revision = instance.revisions.filter(current=True).select_for_update().first() - current_revision.current = None - current_revision.save() - - process_revisions_for_item(instance, revision_data) - - return instance + # We never update, we always update in the create method + raise NotImplementedError() class CollectionItemDepSerializer(serializers.ModelSerializer): diff --git a/django_etesync/views.py b/django_etesync/views.py index 034eaed..24b62fa 100644 --- a/django_etesync/views.py +++ b/django_etesync/views.py @@ -187,8 +187,10 @@ class CollectionItemViewSet(BaseViewSet): # We can't have destroy because we need to get data from the user (in the body) such as hmac. return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED) + def update(self, request, collection_uid=None, uid=None): + return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED) + def partial_update(self, request, collection_uid=None, uid=None): - # FIXME: implement, or should it be implemented elsewhere? return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED) def list(self, request, collection_uid=None):