Add Photo model & templates

This commit is contained in:
Nyymix 2025-01-05 17:26:09 +02:00
parent 9a483b2a7f
commit f04beb3aaa
9 changed files with 123 additions and 11 deletions

View file

@ -37,6 +37,7 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'imagekit',
'gallery'
]
@ -121,6 +122,12 @@ STATICFILES_DIRS = [
BASE_DIR / 'static',
]
# Media files
MEDIA_URL = 'media/'
MEDIA_ROOT = BASE_DIR / 'media'
# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field

View file

@ -25,4 +25,5 @@ urlpatterns = [
]
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View file

@ -1,8 +1,7 @@
from django.contrib import admin
from imagekit.admin import AdminThumbnail
from gallery.models import Album, City, Location
# Register your models here.
from gallery.models import Album, City, Location, Photo
class CityAdmin(admin.ModelAdmin):
@ -19,6 +18,7 @@ class LocationAdmin(admin.ModelAdmin):
search_fields = ('place', 'city__name',)
list_per_page = 30
class AlbumAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ('name',)}
list_display = ('__str__',)
@ -27,7 +27,15 @@ class AlbumAdmin(admin.ModelAdmin):
list_per_page = 30
class PhotoAdmin(admin.ModelAdmin):
list_display = ('__str__', 'album', 'admin_thumbnail',)
list_display_links = ('__str__',)
list_editable = ('album',)
readonly_fields = ['slug', 'taken_at', 'height', 'width', 'exif', ]
admin_thumbnail = AdminThumbnail(image_field='photo_thumbnail',)
admin.site.register(City, CityAdmin)
admin.site.register(Location, LocationAdmin)
admin.site.register(Album, AlbumAdmin)
admin.site.register(Photo, PhotoAdmin)

View file

@ -1,7 +1,8 @@
# Generated by Django 5.1.4 on 2024-12-29 21:23
# Generated by Django 5.1.4 on 2025-01-05 15:24
import datetime
import django.db.models.deletion
import gallery.models
from django.db import migrations, models
@ -49,4 +50,17 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'Albums',
},
),
migrations.CreateModel(
name='Photo',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('slug', models.CharField(editable=False, max_length=15, verbose_name='Photo Slug')),
('photo', models.ImageField(height_field='height', upload_to=gallery.models.Photo._get_upload_path, verbose_name='Photo', width_field='width')),
('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, editable=False, null=True, verbose_name='Taken at')),
('exif', models.JSONField(blank=True, editable=False, null=True, verbose_name='Exif Metadata')),
('album', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='photos', to='gallery.album', verbose_name='Album')),
],
),
]

View file

@ -1,8 +1,14 @@
from datetime import datetime
import os
from datetime import datetime, timedelta
from django.db import models
from django.urls import reverse
from django.utils.text import slugify
from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToFill
from PIL import Image
from gallery.exif import Exif
# Create your models here.
@ -53,3 +59,40 @@ class Album(models.Model):
def __str__(self):
return '{}'.format(self.name)
class Photo(models.Model):
def _get_upload_path(instance, filename):
return os.path.join('albums', str(instance.album.slug), filename)
def _generate_unique_slug(self, datetime_taken=None):
datetime_taken = datetime_taken or datetime.now()
slug = datetime_taken.strftime('%y%m%d%H%M%S')
counter = 0
while Photo.objects.filter(album=self.album, slug=slug).exists():
counter += 1
slug = (datetime_taken + timedelta(seconds=counter)).strftime('%y%m%d%H%M%S')
return slug
album = models.ForeignKey(Album, on_delete=models.CASCADE, related_name='photos', verbose_name="Album")
slug = models.CharField(max_length=15, editable=False, verbose_name="Photo Slug")
photo = models.ImageField(upload_to=_get_upload_path, height_field='height', width_field='width', verbose_name="Photo")
photo_thumbnail = ImageSpecField(source='photo', processors=[ResizeToFill(100, 100)], format='JPEG', options={'quality': 70})
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")
def save(self, *args, **kwargs):
self.exif_data = Exif(self.photo.file)
datetime_taken = getattr(self.exif_data, 'datetimeoriginal', datetime.now)()
self.slug = self.slug or self._generate_unique_slug(datetime_taken)
self.taken_at = self.taken_at or datetime_taken
self.exif = getattr(self.exif_data, 'data', None)
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse('gallery:photo_url', kwargs={'album_path': self.album.slug, 'photo_slug': self.slug})
def __str__(self):
return f'{self.slug} ({self.album.name})'

View file

@ -13,5 +13,8 @@
{% block content %}
<h1>{{ album.name }}</h1>
{% for photo in photos %}
<a href="{{ photo.get_absolute_url }}"> {{ photo.slug }} </a>
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,19 @@
{% extends "base.html" %}
<!-- Title -->
{% block title %} Gallery : Photo {% endblock %}
<!-- Breadcrumb -->
{% block breadcrumb %}
<li><a href="{% url 'gallery:albums_url' %}">Albums</a></li>
<li><a href="{{ photo.album.get_absolute_url }}">{{ photo.album.name }}</a></li>
<li>{{ photo.slug }}</li>
{% endblock %}
<!-- Content -->
{% block content %}
<h1>{{ photo.slug }}</h1>
{% endblock %}

View file

@ -9,9 +9,12 @@ urlpatterns = [
# Main page
path('', views.Main.as_view(), name='main_url'),
# Yksittäinen kuva albumissa:
path('albums/<path:album_path>/<int:photo_slug>/', views.PhotoDetail.as_view(), name="photo_url"),
# Albumin yksityiskohdat:
path('albums/<path:album_slug>/', views.AlbumDetail.as_view(), name="album_url"),
# Albumien lista:
path('albums/', views.AlbumsList.as_view(), name='albums_url'),

View file

@ -1,12 +1,11 @@
from django.shortcuts import get_object_or_404, redirect, render
from django.views.generic import DetailView, ListView, TemplateView
from .models import Album
from .models import Album, Photo
# Create your views here.
class Main(TemplateView):
template_name = "gallery/main.html"
@ -23,4 +22,19 @@ class AlbumDetail(DetailView):
template_name = 'gallery/album_detail.html'
def get_object(self, queryset=None):
return get_object_or_404(Album, slug=self.kwargs.get('album_slug'))
return get_object_or_404(Album, slug=self.kwargs.get('album_slug'))
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['photos'] = self.object.photos.all()
return context
class PhotoDetail(DetailView):
model = Photo
template_name = 'gallery/photo_detail.html'
def get_object(self, queryset=None):
return get_object_or_404(Photo, slug=self.kwargs.get('photo_slug'))