Django Rest Framework not updating ImageField with UpdateAPIView

Viewed 771

All other fields are being correctly updated except the ImageField which also does not throw any errors. In the database I can see the image URL being updated, but on the file system there is no changes to the media/ folder. When I use the model with admin.site.register everything works perfectly (hence I should have properly configured MEDIA_ROOT and MEDIA_URL) and I see file uploaded in the media folder.

views.py

class ProfileView(generics.UpdateAPIView, generics.RetrieveAPIView):
    queryset = Profile.objects.all()
    serializer_class = ProfileSerializer
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]

    def put(self, request):
        print(request.data)
        print(request.FILES)
        p = Profile.objects.get(user=request.user)
        serializer = ProfileSerializer(p)
        serializer.update(p, request.data, request.user)
        return Response(serializer.data)
    
    def get(self, request):
        p = Profile.objects.get(user=request.user)
        serializer = ProfileSerializer(p)
        return Response(serializer.data)

models.py

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    firstName = models.CharField(max_length=50)
    lastName = models.CharField(max_length=50)
    dob = models.DateField(null=True, blank=True)
    desc = models.CharField(max_length=500)
    genderChoices = [('M', 'Male'), ('F', 'Female')]
    gender = models.CharField(choices=genderChoices, max_length=1)
    image = models.ImageField(blank=True, null=True)

    def __str__(self):
        return self.firstName + " " + self.lastName


serializers.py

class ProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = Profile
        fields = ('firstName', 'lastName', 'dob', 'desc', 'gender', 'image', )
    
    #TODO: write validators
    def update(self, instance, validated_data, user):
        p = Profile.objects.filter(user=user)
        p.update(
            firstName=validated_data['firstName'],
            lastName=validated_data['lastName'],
            dob=validated_data['dob'],
            gender=validated_data['gender'],
            image=validated_data['image'],
        )

I am using Postman for testing and this is the response I get, with the image URL which is does not exist on visiting it.

https://i.imgur.com/ujzbc95.png

2 Answers

It's because you are using QuerySet.update method to update your image. This method is intended for updating whole queryset. This method writes directly to the database, so it skips a lot of "magic" that normally happens in the background when you're invoking Model.save method. Especially, it skips all the file processing, including saving this file in MEDIA_ROOT directory. Use Model.save instead or handle saving the file in proper location manually.

Also, you don't have to fetch the user in this method, as your serializer was already invoked with profile as an argument, so it should be available in self.instance inside your update method.

install ==> pip install Pillow

in settings.py add :

import os

MEDIA_URL = "/media/" 
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

in urls.py add:

from django.conf import settings
from django.conf.urls.static import static



if settings.DEBUG:
   urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)

in models.py add:

image = models.ImageField(blank=True, null=True, upload_to='images')

in apiview add context={"request":request}:

def get(self, request):
    p = Profile.objects.get(user=request.user)
    serializer = ProfileSerializer(p, context={"request":request})
    return Response(serializer.data)
Related