From de3660b23cd3db2932f80ff0614c31b6d5a35075 Mon Sep 17 00:00:00 2001
From: Alexandre Iooss <erdnaxe@crans.org>
Date: Sun, 6 Sep 2020 12:04:54 +0200
Subject: [PATCH] Move image upload code to form clean

---
 apps/member/forms.py | 36 ++++++++++++++++++++++++++++++++++++
 apps/member/views.py | 24 +++---------------------
 2 files changed, 39 insertions(+), 21 deletions(-)

diff --git a/apps/member/forms.py b/apps/member/forms.py
index a5d571b6..abefdf2c 100644
--- a/apps/member/forms.py
+++ b/apps/member/forms.py
@@ -1,7 +1,11 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
+import io
+
+from PIL import Image
 from django import forms
+from django.conf import settings
 from django.contrib.auth.forms import AuthenticationForm
 from django.contrib.auth.models import User
 from django.forms import CheckboxSelectMultiple
@@ -77,6 +81,38 @@ class ImageForm(forms.Form):
     width = forms.FloatField(widget=forms.HiddenInput())
     height = forms.FloatField(widget=forms.HiddenInput())
 
+    def clean(self):
+        """Load image and crop"""
+        cleaned_data = super().clean()
+
+        # Image size is limited by Django DATA_UPLOAD_MAX_MEMORY_SIZE
+        image = cleaned_data.get('image')
+        if image:
+            # Let Pillow detect and load image
+            try:
+                im = Image.open(image)
+            except OSError:
+                # Rare case in which Django consider the upload file as an image
+                # but Pil is unable to load it
+                raise forms.ValidationError(_('This image cannot be loaded.'))
+
+            # Crop image
+            x = cleaned_data.get('x', 0)
+            y = cleaned_data.get('y', 0)
+            w = cleaned_data.get('width', 200)
+            h = cleaned_data.get('height', 200)
+            im = im.crop((x, y, x + w, y + h))
+            im = im.resize(
+                (settings.PIC_WIDTH, settings.PIC_RATIO * settings.PIC_WIDTH),
+                Image.ANTIALIAS,
+            )
+
+            # Save
+            image.file = io.BytesIO()
+            im.save(image.file, "PNG")
+
+        return cleaned_data
+
 
 class ClubForm(forms.ModelForm):
     def clean(self):
diff --git a/apps/member/views.py b/apps/member/views.py
index 4534c9e8..2a0394ff 100644
--- a/apps/member/views.py
+++ b/apps/member/views.py
@@ -1,10 +1,8 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
-import io
 from datetime import timedelta, date
 
-from PIL import Image
 from django.conf import settings
 from django.contrib.auth import logout
 from django.contrib.auth.mixins import LoginRequiredMixin
@@ -263,6 +261,7 @@ class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, Det
         return context
 
     def get_success_url(self):
+        """Redirect to profile page after upload"""
         return reverse_lazy('member:user_detail', kwargs={'pk': self.object.id})
 
     def post(self, request, *args, **kwargs):
@@ -271,26 +270,9 @@ class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, Det
         return self.form_valid(form) if form.is_valid() else self.form_invalid(form)
 
     def form_valid(self, form):
+        """Save image to note"""
         image_field = form.cleaned_data['image']
-        x = form.cleaned_data['x']
-        y = form.cleaned_data['y']
-        w = form.cleaned_data['width']
-        h = form.cleaned_data['height']
-        # image crop and resize
-        image_file = io.BytesIO(image_field.read())
-        # ext = image_field.name.split('.')[-1].lower()
-        # TODO: support GIF format
-        image = Image.open(image_file)
-        image = image.crop((x, y, x + w, y + h))
-        image_clean = image.resize((settings.PIC_WIDTH,
-                                    settings.PIC_RATIO * settings.PIC_WIDTH),
-                                   Image.ANTIALIAS)
-        image_file = io.BytesIO()
-        image_clean.save(image_file, "PNG")
-        image_field.file = image_file
-        # renaming
-        filename = "{}_pic.png".format(self.object.note.pk)
-        image_field.name = filename
+        image_field.name = "{}_pic.png".format(self.object.note.pk)
         self.object.note.display_image = image_field
         self.object.note.save()
         return super().form_valid(form)
-- 
GitLab