Django Portfolio Journal

Upload a picture and save it


In order to present my projects on the portfolio page, I need to add images. I've decided that the images should not be smaller than 800 pixels and that the final format should be .jpeg of the image.

I use the Pillow library to verify that the image is a real image. The .verify() method sets the cursor to the end of the file, so I have to reopen the file to continue. The image is verified if the width is more than 800 pixels, then I convert the image to a RGB format to be able to save it later as .jpeg. After resizing the image to a width of 800 pixels. The image will be saved in jpeg format and the filename will be the slug of the image.

# projects/models.py

class Picture(models.Model):
    legend = models.CharField(max_length=100, verbose_name=_("legend of picture"))
    slug = models.SlugField(verbose_name=_("slug of picture"))
    cover_picture = models.BooleanField(default=False, verbose_name=_("cover picture"))
    photo = models.ImageField(
        upload_to="images/",
        verbose_name=_("picture"),
        blank=True,
        null=True,
    )
    published = models.BooleanField(
        default=True, verbose_name=_("picture visible on website")
    )
    project = models.ForeignKey(
        "projects.Project",
        on_delete=models.CASCADE,
        related_name="project_picture",
        verbose_name=_("picture of project"),
    )

    def __str__(self):
        return self.legend

    def save(self, *args, **kwargs):
        if self.photo:
            try:
                img = Image.open(self.photo)
                img.verify()
            except (IOError, SyntaxError) as e:
                raise ValueError(f"The uploaded file is not a valid image. -- {e}")

            # Reopen the image to reset the file pointer
            try:
                img = Image.open(self.photo)
            except (IOError, SyntaxError) as e:
                raise ValueError(
                    f"The uploaded file could not be reopened as an image. -- {e}"
                )

            if img.width > 800:
                if img.mode in ("RGBA", "LA", "P"):
                    img = img.convert("RGB")

                # Calculate new dimensions to maintain aspect ratio with a width of 800
                new_width = 800
                original_width, original_height = img.size
                new_height = int((new_width / original_width) * original_height)

                try:
                    # Resize the image
                    img = img.resize((new_width, new_height), Image.LANCZOS)

                    # Save the image as JPEG
                    temp_img = BytesIO()
                    img.save(temp_img, format="JPEG", optimize=True)
                    temp_img.seek(0)

                    # Change file extension to .jpg
                    original_name, _ = self.photo.name.lower().split(".")
                    img_filename = f"{self.slug}.jpg"

                    # Save the BytesIO object to the ImageField with the new filename
                    self.photo.save(
                        img_filename, ContentFile(temp_img.read()), save=False
                    )
                except (IOError, SyntaxError) as e:
                    raise ValueError(
                        f"An error occurred while processing the image. -- {e}"
                    )

            else:
                raise ValueError(f"The image width is smaller than 800 pixels.")

        super().save(*args, **kwargs)

24.10.2024: There are several things to improve, such as:

    • Use early return to avoid a lot of indentation and else statements.
    • A default picture is missing.

 


Designed by BootstrapMade and modified by DoriDoro