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: