Django-3.1/DRF/React: Unable to save nested images (linked through GenericRelation)

96 views Asked by At

I am building a Django+DRF/React app (simple blog app) and i am facing difficulties saving nested images

Model Structure

  • Model:
    • Post
  • Children:
    • details: ContentType Model ( DRF: save is successfull )
    • images: ContentType Model ( DRF : save is not successfull )

Process

  • Send images from <input type="file" multiple />

  • Process data through FormData

  • Catch request.data and process it

    class PostFormView(generics.RetrieveUpdateDestroyAPIView):
        queryset = Post._objects.is_active()
        serializer_class = PostModelSerializer
        permission_classes = (IsOwnerOr401,)
        parser_classes = (parsers.MultiPartParser,parsers.JSONParser,
                    parsers.FormParser, parsers.FileUploadParser)
        lookup_field = 'slug'
        lookup_url_kwarg = 'slug'
    
        def get_queryset(self):
             return super().get_queryset().annotate(**sharedAnnotations(request=self.request))
    
        def update(self, request, *args, **kwargs):
    
             data = request.data
             _images = data.getlist('images')
             images = []
             for _ in _images:
                 if isinstance(_, dict):
                     images.append(images)
                     continue
                 images.append({'image': _, 'object_id': self.get_object().pk, 'content_type': self.get_object().get_content_type().pk})
    
              data['images'] = images
    
              print(data)
    
              partial = kwargs.pop('partial', False)
              instance = self.get_object()
              serializer = self.get_serializer(instance, data=data,  partial=partial)
              serializer.is_valid(raise_exception=True)
              self.perform_update(serializer)
    
              if getattr(instance, '_prefetched_objects_cache', None):
                  instance._prefetched_objects_cache = {}
    
              return Response(serializer.data)
    
  • Save images (FAIL):

    class MediaModelSerializer(ContentTypeModelSerializer):
         # inherits object_id & content_type fields just to avoid writing them over and over alongside (create & update fns)
         class Meta:
             model = Media 
             fields='__all__'
    
    
    class PostModelSerializer(WritableNestedModelSerializer):
    
        is_active = serializers.BooleanField(default=True)
        path = serializers.HyperlinkedIdentityField(
        view_name="api:post-detail", lookup_field='slug')
    
        images = MediaModelSerializer(many=True)
        details = DetailModelSerializer(required=False, many=True)
    
        # annotated fields
        is_author = serializers.BooleanField(read_only=True, default=False)
    
        class Meta:
            model = Post
            fields = '__all__'
            read_only_fields = ['is_locked', 'slug', 'user', 'is_author']
    
        def create(self, validated_data):
            return super().create(validated_data)
    
        def update(self, instance, validated_data):
            return super().update(instance, validated_data)
    
  • The print(data) statement from PostFormView.update(self, request, *args, **kwargs) (after manipulation) returns this:

    <QueryDict: {'id': ['8'], ..., 'images': [[{'image': <InMemoryUploadedFile: bmw_3.jpeg (image/jpeg)>, 'object_id': 8, 'content_type': 20}, {'image': <InMemoryUploadedFile: bmw_2.jpeg (image/jpeg)>, 'object_id': 8, 'content_type': 20}, {'image': <InMemoryUploadedFile: bmw_1.jpeg (image/jpeg)>, 'object_id': 8, 'content_type': 20}]]}>
    
  • Server returns 400_BAD_REQUEST because images were not passed to PostModelSerializer

    {"images":["This field is required."]}
    

i've been facing this issue for 3 days and i can't wrap my head around the root cause.

Thank you for your help.

1

There are 1 answers

0
Django DO On

i have been looking all over the internet but i could not find any anwsers so i had to go this way

I have removed the processing part from PostFormView.update(...) and accessed the images directly in the create & update methods of the ModelSerializer. I'll figure out later on how to handle removing these images

Here's the code:

class PostModelSerializer(WritableNestedModelSerializer):

    is_active = serializers.BooleanField(default=True)
    path = serializers.HyperlinkedIdentityField(
        view_name="api:post-detail", lookup_field='slug')

    images = MediaModelSerializer(read_only=True, many=True)
    details = DetailModelSerializer(required=False, many=True)

    # annotated fields
    is_author = serializers.BooleanField(read_only=True, default=False)

    class Meta:
        model = Post
        fields = '__all__'
        read_only_fields = ['is_locked', 'slug', 'user', 'is_author']

    def create(self, validated_data):
        instance = super().create(validated_data)

        request = self.context.get('request', None)
        if request:
            try:
                images = request.data.getlist('images')
                for image in images:
                    self.instance.images.create(image=image)
            except Exception as e:
                pass

        return instance

    def update(self, instance, validated_data):
        instance = super().update(instance, validated_data)
        
        request = self.context.get('request', None)
        if request:
            try:
                images = request.data.getlist('images')
                for image in images:
                    self.instance.images.create(image=image)
            except Exception as e:
                pass

        return instance

If anyone has faced this issue before and managed to resolve it, please post your answer below.

Thank you !