Django Rest Framework - add/remove to a list I have a model Collection which has a many to many relation to a model Item.

I want to be able to add or remove items to this collection using Django Rest Framework.

Option1 - make an action:

class CollectionViewSet(viewsets.ModelViewSet):
    queryset = Collection.objects.all()
    serializer_class = CollectionSerializer

    @action()
    def update_items(self, request, **kwargs):
        collection = self.get_object()
        add_items_id = request.DATA.pop('add_items', [])
        remove_items_id = request.DATA.pop('remove_items', [])
        items_add = Item.objects.filter(id__in=add_items_id).all()
        collection.items.add(*items_add)
        items_remove = Item.objects.filter(id__in=remove_items_id).all()
        collection.items.remove(*items_remove)
        return Response()


I see two downsides with this:


I cannot make one single request to update the collection with items and other fields (not without also modifying these.
I do not get the "API documentation" for free.


Option2 - override the update method and use two different serializers

class CollectionSerializer(serializers.HyperlinkedModelSerializer):
    items = ItemSerializer(many=True, read_only=True)

    class Meta:
        model = Collection
        fields = ('id', 'title', 'items')


class CollectionUpdateSerializer(serializers.HyperlinkedModelSerializer):
    add_items = serializers.PrimaryKeyRelatedField(many=True, source='items', queryset=Item.objects.all())
    remove_items = serializers.PrimaryKeyRelatedField(many=True, source='items', queryset=Item.objects.all())

    class Meta:
        model = Collection
        fields = ('id', 'title', 'add_items', 'remove_items')


class CollectionViewSet(viewsets.ModelViewSet):
    queryset = Collection.objects.all()

    def update(self, request, *args, **kwargs):
        collection = self.get_object()
        add_items_id = request.DATA.pop('add_items', [])
        remove_items_id = request.DATA.pop('remove_items', [])
        items_add = Item.objects.filter(id__in=add_items_id).all()
        collection.items.add(*items_add)
        items_remove = Item.objects.filter(id__in=remove_items_id).all()
        collection.items.remove(*items_remove)
        # fool DRF to set items to the new list of items (add_items/remove_items has source 'items')
        request.DATA['add_items'] = collection.items.values_list('id', flat=True)
        request.DATA['remove_items'] = request.DATA['add_items']
        return super().update(request, *args, **kwargs)

    def get_serializer_class(self):
        if self.request.method == "PUT":
            return CollectionUpdateSerializer
        return CollectionSerializer


Unfortunately, option2 has this horrible and inefficient hack. Here is why:

Super is called to handle the other properties (in this case only 'title'), it will see that add_items/remove_items is on the source 'items', (and at this point add_items/remove_items params would be empty) so it would remove all items. 

What would be the canonical way to handle add/remove of items in a list via django-rest-framework, such that we still get:
- 1 request to update the object (i.e. not via a separate action request)
- re-using DRF patterns / auto-generated API
If the collection is at another model then you must setup the serializer with the 'many' kwarg.

class CollectionContainerViewSet(viewsets.ModelViewSet):
    queryset = CollectionContainer.objects.all()
    collection_ids = serializers.PrimaryKeyRelatedField(
                      many=true,
                      queryset=Collection.objects.all()
                    )


Or maybe change the collection to accept updating many?

class CollectionViewSet(viewsets.ModelViewSet):
    queryset = Collection.objects.all()
    serializer_class = CollectionSerializer(many=True)


I guess that in most situations you don't explicitly add or remove, but you are change the collection to a known state - containing a specific set of items. If that is not the case and you really must add and remove explicitly I think it is better to have two actions, one for adding and one for removing items by id.