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

View file

@ -25,4 +25,5 @@ urlpatterns = [
] ]
if settings.DEBUG: 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 django.contrib import admin
from imagekit.admin import AdminThumbnail
from gallery.models import Album, City, Location from gallery.models import Album, City, Location, Photo
# Register your models here.
class CityAdmin(admin.ModelAdmin): class CityAdmin(admin.ModelAdmin):
@ -19,6 +18,7 @@ class LocationAdmin(admin.ModelAdmin):
search_fields = ('place', 'city__name',) search_fields = ('place', 'city__name',)
list_per_page = 30 list_per_page = 30
class AlbumAdmin(admin.ModelAdmin): class AlbumAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ('name',)} prepopulated_fields = {'slug': ('name',)}
list_display = ('__str__',) list_display = ('__str__',)
@ -27,7 +27,15 @@ class AlbumAdmin(admin.ModelAdmin):
list_per_page = 30 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(City, CityAdmin)
admin.site.register(Location, LocationAdmin) admin.site.register(Location, LocationAdmin)
admin.site.register(Album, AlbumAdmin) 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 datetime
import django.db.models.deletion import django.db.models.deletion
import gallery.models
from django.db import migrations, models from django.db import migrations, models
@ -49,4 +50,17 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'Albums', '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.db import models
from django.urls import reverse from django.urls import reverse
from django.utils.text import slugify 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. # Create your models here.
@ -53,3 +59,40 @@ class Album(models.Model):
def __str__(self): def __str__(self):
return '{}'.format(self.name) 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 %} {% block content %}
<h1>{{ album.name }}</h1> <h1>{{ album.name }}</h1>
{% for photo in photos %}
<a href="{{ photo.get_absolute_url }}"> {{ photo.slug }} </a>
{% endfor %}
{% endblock %} {% 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 # Main page
path('', views.Main.as_view(), name='main_url'), 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: # Albumin yksityiskohdat:
path('albums/<path:album_slug>/', views.AlbumDetail.as_view(), name="album_url"), path('albums/<path:album_slug>/', views.AlbumDetail.as_view(), name="album_url"),
# Albumien lista: # Albumien lista:
path('albums/', views.AlbumsList.as_view(), name='albums_url'), 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.shortcuts import get_object_or_404, redirect, render
from django.views.generic import DetailView, ListView, TemplateView from django.views.generic import DetailView, ListView, TemplateView
from .models import Album from .models import Album, Photo
# Create your views here. # Create your views here.
class Main(TemplateView): class Main(TemplateView):
template_name = "gallery/main.html" template_name = "gallery/main.html"
@ -23,4 +22,19 @@ class AlbumDetail(DetailView):
template_name = 'gallery/album_detail.html' template_name = 'gallery/album_detail.html'
def get_object(self, queryset=None): 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'))