Rubick
Rubick

Reputation: 309

Update objects with DRF Serializer with data from JSON

I get a JSON response with data from multiple objects, run everything through the serializer and call serializer.save() which creates new objects but doesnt update the existing ones.

view

data = JSONParser().parse(request)
serializer = CellCESaveSerializer(data=data, many = True, context = {'descriptoridlist' : descriptoridlist}, partial=True)
if serializer.is_valid():
    serializer.save()
    return JsonResponse(serializer.data, safe=False)

the serializer

class StringArrayField(ListField):
    """
    String representation of an array field.
    """
    def to_representation(self, obj):
        obj = super().to_representation(obj)
        # convert list to string
        return ",".join([str(element) for element in obj])

    def to_internal_value(self, data):
        data = data.rstrip(",")  # Needed because of JSON response
        data = data.split(",")  # Split string to list
        data = [int(i) for i in data] # convert str to int
        did = self.context['descriptoridlist']
        x = (did,data)   # Create Array
        return super().to_internal_value(x)

class CellCESaveSerializer(serializers.ModelSerializer):
    row = serializers.SlugRelatedField(slug_field="name",queryset=Descriptor.objects.all() ) # foreignkey mit namen
    val = StringArrayField()   

    class Meta:
        model = CellCE
        fields = ('row','val')

How can i update the existing objects without creating new objects?

EDIT 1:

After adding @neverwalkaloner changes i realised i need to use ListSerializer to update multiple objects. I adjusted my code with help of the DRF documentation. (I changed the serializer to a ListSerializer, added the id Field and the update method.)

serializer:

class CellCESaveSerializer(serializers.ListSerializer):
    row = serializers.SlugRelatedField(slug_field="name",queryset=Descriptor.objects.all()) # foreignkey mit namen
    val = StringArrayField()
    id = serializers.IntegerField()


    class Meta:
        model = CellCE
        fields = ('row','val')

    def update(self, instance, validated_data):
        CellCE_mapping = {CellCE.id: CellCE for CellCE in instance}
        data_mapping = {item['id']: item for item in validated_data}

        # Perform creations and updates.
        ret = []
        for CellCE_id, data in data_mapping.items():
            CellCE = CellCE_mapping.get(CellCE_id, None)
            if CellCE is None:
                ret.append(self.child.create(data))
            else:
                ret.append(self.child.update(CellCE, data))

        # Perform deletions.
        for CellCE_id, CellCE in CellCE_mapping.items():
            if CellCE_id not in data_mapping:
                CellCE.delete()

        return ret

I get the error:

AssertionError: `child` is a required argument.

What am i missing?

Upvotes: 4

Views: 6350

Answers (2)

Nguyen Quang Trung
Nguyen Quang Trung

Reputation: 105

No need to use ListSerializer because when you pass "many=True", it also use ListSerializer class. If you want to update once object, please include "id" field and pass its value in input data.

Upvotes: -1

neverwalkaloner
neverwalkaloner

Reputation: 47354

Serializer's save() method check if serializer's self.instance is empty or not. If self.instance is empty save() will call create() method, otherwise it will call update() method.

So to update instance with serializer, you need to pass as it's first argument updating object:

obj = CellCE.objects.get(id='some_id') # Object wich will be updated
serializer = CellCESaveSerializer(obj, data=data, many = True, context = {'descriptoridlist' : descriptoridlist}, partial=True)
if serializer.is_valid():
    serializer.save()
    return JsonResponse(serializer.data, safe=False)

Upvotes: 2

Related Questions