import os from datetime import datetime from django.db import models from django.db.models.signals import post_save from django.dispatch import receiver from django.urls import reverse from django.utils.text import slugify from imagekit.models import ImageSpecField from imagekit.processors import ResizeToFit from gallery.exif import Exif from gallery.models import Album def get_upload_path(instance, filename): return os.path.join('albums', str(instance.album.slug), filename) class Photo(models.Model): album = models.ForeignKey(Album, on_delete=models.CASCADE, related_name='photos', verbose_name="Album") slug = models.CharField(max_length=50, verbose_name="Photo Slug") photo = models.ImageField(upload_to=get_upload_path, height_field='height', width_field='width', verbose_name="Photo") photo_sm = ImageSpecField(source='photo', processors=[ResizeToFit(320, 320)], format='JPEG', options={'quality': 70}) photo_md = ImageSpecField(source='photo', processors=[ResizeToFit(720, 720)], format='JPEG', options={'quality': 80}) photo_bg = ImageSpecField(source='photo', processors=[ResizeToFit(1920, 1920)], format='JPEG', options={'quality': 90}) width = models.PositiveIntegerField(default=0, editable=False, verbose_name="Photo Width") height = models.PositiveIntegerField(default=0, editable=False, verbose_name="Photo Height") taken_at = models.DateTimeField(blank=True, null=True, editable=False, verbose_name="Taken at") exif = models.JSONField(blank=True, null=True, editable=False, verbose_name="Exif Metadata") is_favorite = models.BooleanField(default=False, verbose_name="Is Favorite") views = models.PositiveIntegerField(default=0, verbose_name="Views") likes = models.PositiveIntegerField(default=0, verbose_name="Likes") @property def orientation(self): return "Portrait" if self.height > self.width else "Landscape" @property def aspect_ratio(self): return self.width / self.height def save(self, *args, **kwargs): if not self.slug: self.slug = os.path.basename(self.photo.name) super().save(*args, **kwargs) def add_like(self): self.likes += 1 self.save(update_fields=['likes']) def add_view(self): self.views += 1 self.save(update_fields=['views']) def get_next(self): return self.__class__.objects.filter(taken_at__gt=self.taken_at, album=self.album).order_by('taken_at').first() def get_prev(self): return self.__class__.objects.filter(taken_at__lt=self.taken_at, album=self.album).order_by('-taken_at').first() def get_absolute_url(self): return reverse('gallery:photo_url', kwargs={'album_slug': self.album.slug, 'photo_slug': self.slug}) class Meta: unique_together = ('album', "slug") verbose_name_plural = "Photos" ordering = ('-taken_at',) def __str__(self): return f'{self.slug} ({self.orientation}) {self.is_favorite}' def create_photo(sender, instance, created, **kwargs): if created: """ Add exif metadata """ exif = Exif(instance.photo.path) instance.exif = exif.data """ Update taken datetime """ instance.taken_at = exif.datetimeoriginal('Europe/Helsinki') instance.save() post_save.connect(create_photo, sender=Photo)