Using .update with nested Serializer to post Image

787 views Asked by At

I have an ImageField. When I update it with the .update command, it does not properly save. It validates, returns a successful save, and says it is good. However, the image is never saved (I don't see it in my /media like I do my other pictures), and when it is later served, it is at /media/Raw%@0Data where there is no picture. When images are stored using a post it stores properly though. Any idea what is wrong, does it have to do with the nested serializer?

class MemberProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = MemberProfile
        fields = (
            'profile_image',
            'phone_number',
            'is_passenger',
            'is_owner',
            'is_captain',
            'date_profile_created',
            'date_profile_modified',
        )
class AuthUserModelSerializer(serializers.ModelSerializer):
    member_profile = MemberProfileSerializer(source='profile')

    class Meta:
        model = get_user_model()
        fields = ('id',
                  'username',
                  'password',
                  'email',
                  'first_name',
                  'last_name',
                  'is_staff',
                  'is_active',
                  'date_joined',
                  'member_profile',
                  )

    def update(self, instance, validated_data):
        profile_data = validated_data.pop('profile')
        for attr, value in validated_data.items():
            if attr == 'password':
                instance.set_password(value)
            else:
                setattr(instance, attr, value)
        instance.save()
        if not hasattr(instance, 'profile'):
            MemberProfile.objects.create(user=instance, **profile_data)
        else:
            #This is the code that is having issues
            profile = MemberProfile.objects.filter(user=instance)
            profile.update(**profile_data)
        return instance

Above, you see where profile = MemberProfile.objects.filter(user=instance) and then the update command. That is not properly saving the image in accordance with the model.

class MemberProfile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL, unique=True, related_name='profile')
    profile_image = models.ImageField(
        upload_to=get_upload_path(instance="instance",
                                  filename="filename",
                                  path='images/profile/'),
        blank=True)
1

There are 1 answers

0
Mark Lavin On BEST ANSWER

As noted in the docs, .update() doesn't call the model .save() or fire the post_save/pre_save signals for each matched model. It almost directly translates into a SQL UPDATE statement. https://docs.djangoproject.com/en/1.8/ref/models/querysets/#update

Finally, realize that update() does an update at the SQL level and, thus, does not call any save() methods on your models, nor does it emit the pre_save or post_save signals (which are a consequence of calling Model.save()).

While it isn't obvious from the docs, the uploaded file is saved to disk as part of the model .save() as well: https://docs.djangoproject.com/en/1.8/topics/files/#using-files-in-models

The file is saved as part of saving the model in the database, so the actual file name used on disk cannot be relied on until after the model has been saved.

This means you can use .update() to directly change the path value stored in the DB column, but it assumes that the file has already been saved to disk in that location.

The easiest way to work around this issue is to call .save() in both both paths. .create() already calls .save() so you would need to change the .update() version to something such as:

for key, value in update_data.items():
    setattr(instance.profile, key, value)
instance.profile.save(update_fields=update_data.keys())