0

I have an UpdateView where the user can update/create their profile details. The details include the profile picture, bio, and gender. I want to be able to take the user's uploaded profile image, crop it, and then save it. However, in my current implementation it only saves the image to a path I specify and that's it.

I have tried to add a save method in my models view to resize the image, however, I get this error: 'Image' object has no attribute '_committed' Here is my code:

class Profile(models.Model):
    GENDER_CHOICES = (
        ('M', 'Male'),
        ('F', 'Female'),
    )
    user    = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
    bio     = models.CharField(max_length=200, null=True)
    avatar  = models.ImageField(upload_to="img/path")
    gender  = models.CharField(max_length=1, choices=GENDER_CHOICES, null=True)    

    def save(self, *args, **kwargs):
        if self.avatar:
            image = Image.open(self.avatar)

            self.avatar = image.resize((200, 200), Image.ANTIALIAS)
        super(Profile, self).save(*args, **kwargs)

UpdateView

class ProfileSettings(UpdateView):
    model = Profile
    template_name = 'profile/settings.html'
    form_class = ProfileForm
    success_url = reverse_lazy('profile:settings')

    def post(self, request, *args, **kwargs):
        form = self.form_class(request.POST, request.FILES)

        if form.is_valid():
            bio = form.cleaned_data['bio']
            gender = form.cleaned_data['gender']
            avatar = form.cleaned_data['avatar']

            Profile.objects.update_or_create(user=self.request.user, defaults={'avatar':avatar, 'bio':bio, 'gender':gender})

        return HttpResponseRedirect(self.success_url)
user2896120
  • 3,180
  • 4
  • 40
  • 100

1 Answers1

2

I think you can update your save method like this:

def save(self, *args, **kwargs):
    super(Profile, self).save(*args, **kwargs)  
    if self.avatar:
       image = Image.open(self.avatar)
       height, width = image.size
       if height==200 and width==200:
           image.close()
           return
       image = image.resize((200, 200), Image.ANTIALIAS)
       image.save(self.avatar.path)
       image.close()
ruddra
  • 50,746
  • 7
  • 78
  • 101
  • Hmm, I'm getting this error: `'ImageFieldFile' object has no attribute 'image'` – user2896120 Jan 31 '19 at 05:11
  • Opps, my bad. Updated the answer – ruddra Jan 31 '19 at 05:12
  • This works! However, To make this question simpler, I removed some details. In `ImageField` I also have `storage=OverwriteStorage()` which overwrites images with the same name. However, when I try to upload an image with the same name as an already uploaded image I get this: `The process cannot access the file because it is being used by another process:` – user2896120 Jan 31 '19 at 05:17
  • Maybe you can look into this answer: https://stackoverflow.com/questions/9522759/imagefield-overwrite-image-file-with-same-name. – ruddra Jan 31 '19 at 05:21
  • from another SO Question: https://stackoverflow.com/questions/43659103. `File access in Windows requires explicitly declaring whether read/execute, write/append, and delete/rename access will be shared with subsequent attempts to open the file, including within the same process. You have to find out which file it's complaining about and see which process has an open handle for it (e.g. using Process Explorer). If it's another process, closing it will solve the immediate problem. If it's your own process, you can modify the shared access or how you're using the file to work around the problem` – ruddra Jan 31 '19 at 05:22
  • 1
    Ahh, it went away after a second attempt. Do you think after opening the image, there should be a close call as well? – user2896120 Jan 31 '19 at 05:31
  • Also, I think this is relevant as well. What if the image is less than 200 either width or height? I added the logic to return a `ValidationError`, but it uploads the image anyway – user2896120 Jan 31 '19 at 05:45
  • Um, it would be great if you could ask a new question for this and share relevant code there. Thanks :) – ruddra Jan 31 '19 at 05:57
  • Sure, I added another question: https://stackoverflow.com/questions/54454515/not-allowing-images-small-than-certain-dimensions – user2896120 Jan 31 '19 at 06:22