Update M2M relationship django rest framework (many=true)

前端 未结 3 1504
北海茫月
北海茫月 2021-01-06 14:10

I try to update a channel:

PUT
content [{\'url\': \'http://localhost:8000/api/movies/2\', \'title\': u\'Ariel\', \'backdrop_path\': u\'/z2QUexmccqrvw1kDMw3R8         


        
相关标签:
3条回答
  • 2021-01-06 14:31

    If you want to update the nested relation you can do like this,

          class SchoolSerializer(serializers.HyperlinkedModelSerializer):
    
                    students = StudentSerializer(many=True, read_only=True)
                    students_ids = serializers.PrimaryKeyRelatedField(many=True,\
                    read_only=False, queryset=Student.objects.all(),\ 
                    source='students')
    
                    class Meta:
                        model = School
                        fields = ('name', 'image', 'address', 'url',\
                        'students', 'students_ids')
    

    use PrimaryKeyRelatedField this will allow you to create, update, nested relations (Many-to-Many field) by just passing a list of id's students will give you nested data, students_ids can be used for write operations

    0 讨论(0)
  • 2021-01-06 14:32

    This is a little outdated, but for future people looking for a potential solution to this problem, I found it useful to patch viewset.

    You cannot read post params twice, which is the only thing preventing one from passing a Primary key for the related update and performing the m2m update in post_save

    I made a custom viewset based on ModelViewSet with updated create and update statements:

    In your app, you can create a module called viewsets.py:

    # -*- coding: utf-8 -*-
    
    from rest_framework import mixins
    from rest_framework import status
    from rest_framework.response import Response
    from rest_framework.viewsets import GenericViewSet
    
    class RelatedCreateModelMixin(mixins.CreateModelMixin):
    
        '''
        Monkey patch the UpdateModel for ModelViewSet Mixin to support data
        transferrance from pre - to - save - to - post
        '''
    
        def create(self, request, *args, **kwargs):
            data = request.DATA
            serializer = self.get_serializer(data=data, files=request.FILES)
    
            if serializer.is_valid():
                self.pre_save(serializer.object, data=data)
                self.object = serializer.save(force_insert=True)
                self.post_save(self.object, created=True, data=data)
                headers = self.get_success_headers(serializer.data)
                return Response(serializer.data, status=status.HTTP_201_CREATED,
                                headers=headers)
    
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    class RelatedUpdateModelMixin(mixins.UpdateModelMixin):
    
        def update(self, request, *args, **kwargs):
            partial = kwargs.pop('partial', False)
            self.object = self.get_object_or_none()
    
            data = request.DATA
    
            serializer = self.get_serializer(self.object, data=data,
                                             files=request.FILES, partial=partial)
    
            if not serializer.is_valid():
                return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
            try:
                self.pre_save(serializer.object, data=data)
            except ValidationError as err:
                # full_clean on model instance may be called in pre_save,
                # so we have to handle eventual errors.
                return Response(err.message_dict, status=status.HTTP_400_BAD_REQUEST)
    
            if self.object is None:
                self.object = serializer.save(force_insert=True)
                self.post_save(self.object, data=data, created=True)
                return Response(serializer.data, status=status.HTTP_201_CREATED)
    
            self.object = serializer.save(force_update=True)
            self.post_save(self.object, created=False)
            return Response(serializer.data, status=status.HTTP_200_OK)
    
    
    class RelatedModelViewSet(RelatedCreateModelMixin,
                              mixins.RetrieveModelMixin,
                              RelatedUpdateModelMixin,
                              mixins.DestroyModelMixin,
                              mixins.ListModelMixin,
                              GenericViewSet):
        pass
    

    Then, in your view, use instead:

    from MYAPP import viewsets
    

    Which allows you to do something along the lines of:

    def post_save(self, obj, *args, **kwargs):
        data = kwargs.get('data')
        model_id = data.get('id')
        parent_obj = Model.objects.get(id=model_id)
        method = self.request.method
        if method == 'POST':
            parent_obj.m2m.add(obj)
        elif method == 'PUT':
            parent_obj.m2m.remove(obj)
    

    Not the most elegant solution, but I find it preferable to writing a custom serializer

    0 讨论(0)
  • 2021-01-06 14:45

    As you can read here, nested relations currently don't support write operations. Use HyperlinkedRelatedField instead or write a custom serializer, that implements the features you need.

    0 讨论(0)
提交回复
热议问题