From 07a164f9fba4c8f0ef33fd499f16133372b3c2a8 Mon Sep 17 00:00:00 2001 From: KwikKill Date: Fri, 4 Oct 2024 23:24:09 +0200 Subject: [PATCH] Add a new field to convert images to webp --- insalan/components/image_field.py | 33 +++++++++++++++++++++++++ insalan/partner/models.py | 4 ++- insalan/pizza/models.py | 4 ++- insalan/tournament/models/caster.py | 4 ++- insalan/tournament/models/event.py | 4 ++- insalan/tournament/models/tournament.py | 3 ++- insalan/user/models.py | 4 ++- 7 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 insalan/components/image_field.py diff --git a/insalan/components/image_field.py b/insalan/components/image_field.py new file mode 100644 index 00000000..f3c93b06 --- /dev/null +++ b/insalan/components/image_field.py @@ -0,0 +1,33 @@ +import os +from io import BytesIO + +from django.core.exceptions import ValidationError +from django.db import models +from PIL import Image + +class ImageField(models.ImageField): + """ + A subclass of Django's ImageField that convert the image to a webp format + """ + + def __init__(self, *args, **kwargs): + self.convert_to_webp = kwargs.pop("convert_to_webp", True) + super().__init__(*args, **kwargs) + + def pre_save(self, model_instance, add): + file = super().pre_save(model_instance, add) + if self.convert_to_webp: + try: + img = Image.open(file) + if img.format != "WEBP": + buffer = BytesIO() + img.save(buffer, format="WEBP") + buffer.seek(0) + file.save( + os.path.basename(os.path.splitext(file.name)[0]) + ".webp", + buffer, + save=False, + ) + except Exception as e: + raise ValidationError(f"Invalid image file: {e}") from e + return file diff --git a/insalan/partner/models.py b/insalan/partner/models.py index 6bba5ee8..cef482c2 100644 --- a/insalan/partner/models.py +++ b/insalan/partner/models.py @@ -4,6 +4,8 @@ from django.db import models from django.utils.translation import gettext_lazy as _ +from insalan.components.image_field import ImageField + class Partner(models.Model): class PartnerType(models.TextChoices): """There are two types of sponsors""" @@ -22,7 +24,7 @@ class Meta: max_length=200, verbose_name=_("Nom du partenaire/sponsor") ) url: models.URLField = models.URLField(verbose_name=_("URL")) - logo: models.FileField = models.FileField( + logo: models.FileField = ImageField( verbose_name=_("Logo"), upload_to="partners", validators=[ diff --git a/insalan/pizza/models.py b/insalan/pizza/models.py index 00e7f1e2..e77c55a9 100644 --- a/insalan/pizza/models.py +++ b/insalan/pizza/models.py @@ -15,6 +15,8 @@ from django.utils.translation import gettext_lazy as _ from django.contrib.postgres.fields import ArrayField +from insalan.components.image_field import ImageField + class PaymentMethod(models.TextChoices): """ Payment method choices @@ -52,7 +54,7 @@ class Meta: blank=True, null=True, ) - image: models.FileField = models.FileField( + image: models.FileField = ImageField( verbose_name=_("Image"), upload_to="pizzas", validators=[ diff --git a/insalan/tournament/models/caster.py b/insalan/tournament/models/caster.py index f90ad1ca..edbc04c2 100644 --- a/insalan/tournament/models/caster.py +++ b/insalan/tournament/models/caster.py @@ -2,6 +2,8 @@ from django.core.validators import FileExtensionValidator from django.utils.translation import gettext_lazy as _ +from insalan.components.image_field import ImageField + class Caster(models.Model): """ A Caster is someone that can cast a tournament. @@ -12,7 +14,7 @@ class Caster(models.Model): blank=False, verbose_name=_("Nom du casteur") ) - image = models.FileField( + image = ImageField( verbose_name=_("Photo de profil"), blank=True, null=True, diff --git a/insalan/tournament/models/event.py b/insalan/tournament/models/event.py index 6011c4dc..bdf3faa7 100644 --- a/insalan/tournament/models/event.py +++ b/insalan/tournament/models/event.py @@ -10,6 +10,8 @@ from . import tournament +from insalan.components.image_field import ImageField + class Event(models.Model): """ An Event is any single event that is characterized by the following: @@ -41,7 +43,7 @@ class Event(models.Model): validators=[MinValueValidator(1), MaxValueValidator(12)], ) ongoing = models.BooleanField(verbose_name=_("En cours"), default=False) - logo: models.FileField = models.FileField( + logo: models.FileField = ImageField( verbose_name=_("Logo"), blank=True, null=True, diff --git a/insalan/tournament/models/tournament.py b/insalan/tournament/models/tournament.py index 2ae98e87..6e872a8e 100644 --- a/insalan/tournament/models/tournament.py +++ b/insalan/tournament/models/tournament.py @@ -12,6 +12,7 @@ from django.contrib.postgres.fields import ArrayField from . import team, caster, group, bracket, swiss +from insalan.components.image_field import ImageField def in_thirty_days(): """Return now + 30 days""" @@ -51,7 +52,7 @@ class Tournament(models.Model): default=in_thirty_days, null=False, ) - logo: models.FileField = models.FileField( + logo: models.FileField = ImageField( verbose_name=_("Logo"), blank=True, null=True, diff --git a/insalan/user/models.py b/insalan/user/models.py index 5aedc1f5..119efcba 100644 --- a/insalan/user/models.py +++ b/insalan/user/models.py @@ -11,6 +11,8 @@ from django.utils.translation import gettext_lazy as _ from django.core.validators import FileExtensionValidator +from insalan.components.image_field import ImageField + class UserManager(BaseUserManager): """ Managers the User objects (kind of like a serializer but not quite that) @@ -75,7 +77,7 @@ def __init__(self, *args, **kwargs): USERNAME_FIELD = "username" EMAIL_FIELD = "email" - image = models.FileField( + image = ImageField( verbose_name=_("photo de profil"), blank=True, null=True,