Galerie: začátek podpory jiných typů [WIP]
Co funguje: ~~nic, protože není vygenerovaná migrace~~ Nejspíš lecos, ale nejde to otestovat. Speciálně, pokud zavolám funkce z `galerie.typy` ručně ze shellu, tak se to popřeškáluje a výsledná URL jde skutečně vyrenderovat na webu (resp. šlo v nějaké průběžné verzi před zavedením templatetagu). TODO: - ve views a modelech se pořád ještě vyskytuje `obrazek_stredni` a `obrazek_velky` - nevím, co dělá Admin a nahrávání obrázků - chybí funkce na tipování typů nahraných obrázků (a její použití v kódu) - podpora pro videjka zatím není vůbec (jen připravený kód) - DummyBazmek nemá použitelné placeholdery (představuji si SVG s nějakým vykřičníkem a odkazem do Admina
This commit is contained in:
parent
b7498b42b2
commit
b25d475148
6 changed files with 169 additions and 40 deletions
|
@ -28,14 +28,14 @@ def prepnout_fotogalerii_do_org_rezimu(modeladmin, request, queryset):
|
||||||
|
|
||||||
class GalerieInline(admin.TabularInline):
|
class GalerieInline(admin.TabularInline):
|
||||||
model = Obrazek
|
model = Obrazek
|
||||||
fields = ['obrazek_velky', 'nazev', 'popis', 'poradi']
|
fields = ['soubor', 'nazev', 'popis', 'poradi']
|
||||||
readonly_fields = ['nazev']
|
readonly_fields = ['nazev']
|
||||||
formfield_overrides = {
|
formfield_overrides = {
|
||||||
models.TextField: {'widget': forms.TextInput},
|
models.TextField: {'widget': forms.TextInput},
|
||||||
}
|
}
|
||||||
|
|
||||||
class ObrazekAdmin(admin.ModelAdmin):
|
class ObrazekAdmin(admin.ModelAdmin):
|
||||||
list_display = ('obrazek_velky', 'nazev', 'popis', 'obrazek_maly_tag', 'poradi')
|
list_display = ('soubor', 'nazev', 'popis', 'poradi')
|
||||||
search_fields = ['nazev','popis']
|
search_fields = ['nazev','popis']
|
||||||
|
|
||||||
class GalerieAdmin(admin.ModelAdmin):
|
class GalerieAdmin(admin.ModelAdmin):
|
||||||
|
|
|
@ -22,10 +22,12 @@ def obrazek_filename_maly():
|
||||||
pass
|
pass
|
||||||
def obrazek_filename_stredni():
|
def obrazek_filename_stredni():
|
||||||
pass
|
pass
|
||||||
|
def obrazek_filename():
|
||||||
|
pass
|
||||||
def obrazek_filename_velky():
|
def obrazek_filename_velky():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def obrazek_filename(self, filename):
|
def galerie_filename(self, filename):
|
||||||
gal = self.galerie
|
gal = self.galerie
|
||||||
cislo_gal = gal.pk
|
cislo_gal = gal.pk
|
||||||
|
|
||||||
|
@ -42,31 +44,32 @@ def obrazek_filename(self, filename):
|
||||||
|
|
||||||
return os.path.join(*cesta)
|
return os.path.join(*cesta)
|
||||||
|
|
||||||
|
# Technicky misnomer: takový `Obrazek` může být i videjko a výhledově i něco dalšího…
|
||||||
class Obrazek(models.Model):
|
class Obrazek(models.Model):
|
||||||
# „originál“ (modulo max. velikost uploadu na web FIXME!)
|
# „originál“ (modulo max. velikost uploadu na web FIXME!)
|
||||||
obrazek_velky = models.FileField(upload_to=obrazek_filename,
|
soubor = models.FileField(upload_to=galerie_filename,
|
||||||
help_text = "Lze vložit libovolně velký obrázek. Ideální je, aby alespoň jeden rozměr měl alespoň 500px.")
|
help_text = "Lze vložit libovolně velký obrázek. Ideální je, aby alespoň jeden rozměr měl alespoň 500px.")
|
||||||
# To, co se zobrazí v galerii jako hlavní obrázek (při prohlížení konkrétního obrázku a jako tittulní obrázek u galerií, které nemají vlastní obrázky (kupř. Vávrovka 2015))
|
class Typ(models.TextChoices):
|
||||||
# obrazek_stredni = ImageSpecField(source='obrazek_velky',
|
OBRAZEK = 'obrazek', 'Obrázek'
|
||||||
# processors=[Transpose(Transpose.AUTO), ResizeToFit(900, 675, upscale=False)],
|
VIDEO = 'video', 'Video'
|
||||||
# options={'quality': 95})
|
NEVIM = 'nevim', 'Neznámý typ'
|
||||||
# Zmenšené obrázky v přehledu obrázků a pod hlavním obrázkem (předchozí/následující)
|
typ = models.CharField('Typ', max_length=16, blank=False, null=False, choices=Typ.choices, default=Typ.NEVIM)
|
||||||
# obrazek_maly = ImageSpecField(source='obrazek_velky',
|
# Filename by default; slouží k řazení
|
||||||
# processors=[Transpose(Transpose.AUTO), ResizeToFit(167, 167, upscale=False)],
|
|
||||||
# options={'quality': 95})
|
|
||||||
nazev = models.CharField('Název', max_length=50, blank=True, null=True)
|
nazev = models.CharField('Název', max_length=50, blank=True, null=True)
|
||||||
|
# ~~Rádoby~~ vtipný popisek od orgů
|
||||||
popis = models.TextField('Popis', blank=True, null=True)
|
popis = models.TextField('Popis', blank=True, null=True)
|
||||||
datum_vlozeni = models.DateTimeField('Datum vložení', auto_now_add=True)
|
datum_vlozeni = models.DateTimeField('Datum vložení', auto_now_add=True)
|
||||||
galerie = models.ForeignKey('Galerie', blank=True, null=True, on_delete=models.SET_NULL)
|
galerie = models.ForeignKey('Galerie', blank=True, null=True, on_delete=models.SET_NULL)
|
||||||
|
# Primární klíč k řazení pro overridování řazení podle názvu
|
||||||
poradi = models.IntegerField('Pořadí', blank=True, null=True)
|
poradi = models.IntegerField('Pořadí', blank=True, null=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.obrazek_velky.name
|
return f'Obrázek {self.nazev} ({self.soubor.name})'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = 'Obrázek'
|
verbose_name = 'Obrázek'
|
||||||
verbose_name_plural = 'Obrázky'
|
verbose_name_plural = 'Obrázky'
|
||||||
ordering = ['nazev']
|
ordering = ['nazev'] #FIXME: zohlednit pořadí a asi i příslušnost ke galerii
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
# obrázek potřebuje název, protože se z něj generuje cesta pro jeho uložení
|
# obrázek potřebuje název, protože se z něj generuje cesta pro jeho uložení
|
||||||
|
@ -75,6 +78,17 @@ class Obrazek(models.Model):
|
||||||
self.nazev = os.path.basename(self.obrazek_velky.name)
|
self.nazev = os.path.basename(self.obrazek_velky.name)
|
||||||
super(Obrazek, self).save(*args, **kwargs)
|
super(Obrazek, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
def jako_bazmek(self):
|
||||||
|
"""
|
||||||
|
Hlavní metoda pro dělání `galerie.typy.ZobrazitelnyBazmek` z DB objektů
|
||||||
|
"""
|
||||||
|
import galerie.typy as typy # pozor, cyklí!
|
||||||
|
match self.typ:
|
||||||
|
case self.Typ.OBRAZEK: return typy.Obrazek(self)
|
||||||
|
case self.Typ.VIDEO: return typy.Video(self)
|
||||||
|
case self.Typ.NEVIM: return typy.DummyBazmek(self)
|
||||||
|
case _: raise ValueError("Neznámý typ obrázku, bug v kódu!")
|
||||||
|
|
||||||
|
|
||||||
class Galerie(models.Model):
|
class Galerie(models.Model):
|
||||||
nazev = models.CharField('Název', max_length=100)
|
nazev = models.CharField('Název', max_length=100)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{% extends "galerie/base.html" %}
|
{% extends "galerie/base.html" %}
|
||||||
|
|
||||||
|
{% load bazmeky %}
|
||||||
|
|
||||||
{% block nadpis1a %}
|
{% block nadpis1a %}
|
||||||
{{galerie.nazev}}: {{ obrazek.popis | default:"Fotka" }}
|
{{galerie.nazev}}: {{ obrazek.popis | default:"Fotka" }}
|
||||||
|
@ -63,11 +64,12 @@
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% endif%}
|
{% endif%}
|
||||||
<span id="nahoru" class="kotva_obrazku"></span>
|
<span id="nahoru" class="kotva_obrazku"></span>
|
||||||
<img src="{{obrazek.obrazek_stredni.url}}"
|
{% zobrazit obrazek.jako_bazmek
|
||||||
height="{{vyska}}"
|
height=vyska
|
||||||
width="{{sirka}}"
|
width=sirka
|
||||||
alt="{{obrazek.popis}}"
|
alt=obrazek.popis
|
||||||
class="obrazek">
|
title=obrazek.popis
|
||||||
|
class=obrazek" %}
|
||||||
|
|
||||||
{% if obrazky_dalsi %}
|
{% if obrazky_dalsi %}
|
||||||
{% with obrazky_dalsi|first as dalsi_obrazek %}
|
{% with obrazky_dalsi|first as dalsi_obrazek %}
|
||||||
|
@ -78,7 +80,7 @@
|
||||||
{% endif%}
|
{% endif%}
|
||||||
</div>
|
</div>
|
||||||
<!--<div>-->
|
<!--<div>-->
|
||||||
<!--<a href="{{ obrazek.obrazek_velky.url }}">Obrázek v plné velikosti</a>-->
|
<!--<a href="{{ obrazek.soubor.url }}">Obrázek v plné velikosti</a>-->
|
||||||
<!--</div>-->
|
<!--</div>-->
|
||||||
|
|
||||||
{# Popisek fotky #}
|
{# Popisek fotky #}
|
||||||
|
@ -109,21 +111,21 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{# nahledy predchozich obrazku #}
|
{# nahledy predchozich obrazku #}
|
||||||
{% for obrazek in obrazky_predchozi %}
|
{% for obrazek in obrazky_predchozi %}
|
||||||
<a href="../{{obrazek.pk}}#nahoru"><img src="{{obrazek.obrazek_maly.url}}" height="100"></a>
|
<a href="../{{obrazek.pk}}#nahoru">{% zmenseny_nahled obrazek.jako_bazmek height=100 %}</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<img src={{obrazek.obrazek_maly.url}}
|
{% zmenseny_nahled obrazek.jako_bazmek
|
||||||
height="{{obrazek.obrazek_maly.height}}"
|
alt=obrazek.popis
|
||||||
width="{{obrazek.obrazek_maly.width}}"
|
class=obrazek
|
||||||
alt="{{obrazek.popis}}"
|
id=prostredni %}
|
||||||
class="obrazek"
|
{# height=obrazek.obrazek_maly.height
|
||||||
id="prostredni">
|
width=obrazek.obrazek_maly.width #}
|
||||||
|
|
||||||
<div class="navigace">
|
<div class="navigace">
|
||||||
{# nahledy nasledujicich obrazku #}
|
{# nahledy nasledujicich obrazku #}
|
||||||
{% for obrazek in obrazky_dalsi %}
|
{% for obrazek in obrazky_dalsi %}
|
||||||
<a href="../{{obrazek.pk}}#nahoru"><img src="{{obrazek.obrazek_maly.url}}" height="100"></a>
|
<a href="../{{obrazek.pk}}#nahoru">{% zmenseny_nahled obrazek.jako_bazmek height=100 %}</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{# odkaz na nasledujici galerii #}
|
{# odkaz na nasledujici galerii #}
|
||||||
{% if nasledujici_galerie %}
|
{% if nasledujici_galerie %}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
{% extends "galerie/base.html" %}
|
{% extends "galerie/base.html" %}
|
||||||
|
|
||||||
|
{% load bazmeky %}
|
||||||
|
|
||||||
{% block nadpis1a %}
|
{% block nadpis1a %}
|
||||||
Galerie {{galerie.nazev}}
|
Galerie {{galerie.nazev}}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -23,7 +25,7 @@ Galerie {{galerie.nazev}}
|
||||||
{% if not obrazky %}
|
{% if not obrazky %}
|
||||||
<div class="galerie_hlavicka">
|
<div class="galerie_hlavicka">
|
||||||
{% if galerie.titulni_obrazek %}
|
{% if galerie.titulni_obrazek %}
|
||||||
<img src="{{ galerie.titulni_obrazek.obrazek_stredni.url }}" class="titulni_obrazek">
|
{% zobrazit galerie.titulni_obrazek.jako_bazmek class=titulni_obrazek %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -52,10 +54,7 @@ Galerie {{galerie.nazev}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
class="podgalerie_nahled">
|
class="podgalerie_nahled">
|
||||||
{% if galerie.titulni_obrazek %}
|
{% if galerie.titulni_obrazek %}
|
||||||
{% with galerie.titulni_obrazek.obrazek_maly as obrazek %}
|
{% zmenseny_nahled galerie.titulni_obrazek.jako_bazmek %}
|
||||||
<img src="{{ obrazek.url }}"
|
|
||||||
/>
|
|
||||||
{% endwith %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="nazev_galerie">
|
<div class="nazev_galerie">
|
||||||
{{ galerie|truncatechars:max_delka_nazvu }}
|
{{ galerie|truncatechars:max_delka_nazvu }}
|
||||||
|
@ -87,13 +86,12 @@ Galerie {{galerie.nazev}}
|
||||||
{% if obrazek.popis %}
|
{% if obrazek.popis %}
|
||||||
title="{{ obrazek.popis }}"
|
title="{{ obrazek.popis }}"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
href="./{{obrazek.pk}}#nahoru" class="galerie_nahled"><span class="vystredeno"></span><img
|
href="./{{obrazek.pk}}#nahoru" class="galerie_nahled">
|
||||||
src="{{obrazek.obrazek_maly.url}}"
|
<span class="vystredeno"></span>
|
||||||
{% if obrazek.popis %}
|
{% zmenseny_nahled obrazek.jako_bazmek %}
|
||||||
title="{{ obrazek.popis }}"
|
{# title=obrazek.popis (a byl tu if, že se použil jen když existoval…)
|
||||||
{% endif %}
|
width=obrazek.obrazek_maly.width
|
||||||
width="{{ obrazek.obrazek_maly.width }}"
|
height=obrazek.obrazek_maly.height #}
|
||||||
height="{{ obrazek.obrazek_maly.height }}" />
|
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<br>
|
<br>
|
||||||
|
|
13
galerie/templatetags/bazmeky.py
Normal file
13
galerie/templatetags/bazmeky.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
"""
|
||||||
|
Pomocné tagy pro zobrazování `galerie.typy.ZobrazitelnyBazmek`. Jen volají příslušnou metodu na bazmeku.
|
||||||
|
"""
|
||||||
|
from django import template
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def zobrazit(bazmek, /, **kwargs):
|
||||||
|
return bazmek.zobrazit(**kwargs)
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def zmenseny_nahled(bazmek, /, **kwargs):
|
||||||
|
return bazmek.zmenseny_nahled(**kwargs)
|
102
galerie/typy.py
Normal file
102
galerie/typy.py
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
"""
|
||||||
|
Naše typy objektů v galeriích.
|
||||||
|
|
||||||
|
V databázi jsou všechny v jedné tabulce, protože se liší jen prezentací navenek. Všechny implementují ~~rozhraní~~ ABC `ZobrazitelnyBazmek`.
|
||||||
|
|
||||||
|
Doporučené použití: TODO
|
||||||
|
"""
|
||||||
|
|
||||||
|
import abc
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from django.utils.safestring import mark_safe, SafeString
|
||||||
|
from django.utils.html import format_html, format_html_join
|
||||||
|
|
||||||
|
from imagekit import ImageSpec
|
||||||
|
from imagekit.cachefiles import ImageCacheFile
|
||||||
|
from imagekit.processors import ResizeToFit, Transpose
|
||||||
|
|
||||||
|
HTML = str | SafeString
|
||||||
|
|
||||||
|
from galerie.models import Obrazek as DbObrazek
|
||||||
|
|
||||||
|
class ZobrazitelnyBazmek(abc.ABC):
|
||||||
|
def __init__(self, db_objekt):
|
||||||
|
self.db_objekt = db_objekt
|
||||||
|
# zkratka
|
||||||
|
self.soubor = db_objekt.soubor
|
||||||
|
|
||||||
|
# Volá se z templatetagu
|
||||||
|
@abc.abstractmethod
|
||||||
|
def zobrazit(self, **kwargs) -> HTML:
|
||||||
|
"""To, co se zobrazí v galerii jako hlavní obrázek (při prohlížení konkrétního obrázku a jako tittulní obrázek u galerií, které nemají vlastní obrázky (kupř. Vávrovka 2015))"""
|
||||||
|
...
|
||||||
|
@abc.abstractmethod
|
||||||
|
def zmenseny_nahled(self, **kwargs) -> HTML:
|
||||||
|
"""Zmenšené obrázky v přehledu obrázků a pod hlavním obrázkem (předchozí/následující)"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
#@abc.abstractmethod
|
||||||
|
def cas_porizeni(self) -> datetime | None:
|
||||||
|
# TODO: použít tohle na automatické řazení věcí. Má vytáhnout datum z metadat
|
||||||
|
raise NotImplementedError("Prosím, naimplementuj hledání času vzniku v metadatech (nebo vrať None).")
|
||||||
|
|
||||||
|
#TODO: nativní rozměry?
|
||||||
|
|
||||||
|
|
||||||
|
### Obrázky ###
|
||||||
|
# Odpovídá původnímu chování (bo se mi nechce vymýšlet novoty…
|
||||||
|
class ObrazekStredniSpec(ImageSpec):
|
||||||
|
"""Specifikace obrázku pro velké zobrazení"""
|
||||||
|
processors = [
|
||||||
|
Transpose(Transpose.AUTO), # Rotuj podle dat v EXIFu
|
||||||
|
ResizeToFit(900, 675, upscale=False),
|
||||||
|
]
|
||||||
|
format = 'JPEG'
|
||||||
|
options = {'quality': 95} # Proč tolik?
|
||||||
|
|
||||||
|
class ObrazekMalySpec(ImageSpec):
|
||||||
|
"""Specifikace obrázku pro náhledy (pod hlavním obrázkem nebo v přehledu galerie)"""
|
||||||
|
processors = [
|
||||||
|
Transpose(Transpose.AUTO),
|
||||||
|
ResizeToFit(167, 167, upscale=False),
|
||||||
|
]
|
||||||
|
format = 'JPEG'
|
||||||
|
options = {'quality': 95}
|
||||||
|
|
||||||
|
class Obrazek(ZobrazitelnyBazmek):
|
||||||
|
"""Obrázek pro zobrazení
|
||||||
|
|
||||||
|
Použije některý z ImageSpec-ů jako popis transformace a ImageCacheFile pro uložení výsledného obrázku.
|
||||||
|
|
||||||
|
Reference: https://django-imagekit.readthedocs.io/en/latest/#defining-specs-outside-of-models
|
||||||
|
Reference: https://django-imagekit.readthedocs.io/en/latest/caching.html
|
||||||
|
"""
|
||||||
|
def zobrazit(self, **kwargs):
|
||||||
|
# Jak se takový cachefile používá je potřeba vyčíst ze zdrojáků?
|
||||||
|
file = ImageCacheFile(ObrazekStredniSpec(source=self.soubor))
|
||||||
|
file.generate()
|
||||||
|
attrs = format_html_join(' ', r'{}="{}"', ((mark_safe(k), v) for k, v in kwargs.items()))
|
||||||
|
html = format_html(r'<img src="{}" {} />', file.url, attrs)
|
||||||
|
return html
|
||||||
|
|
||||||
|
def zmenseny_nahled(self, **kwargs):
|
||||||
|
file = ImageCacheFile(ObrazekMalySpec(source=self.soubor))
|
||||||
|
file.generate()
|
||||||
|
attrs = format_html_join(' ', r'{}="{}"', ((mark_safe(k), v) for k, v in kwargs.items()))
|
||||||
|
html = format_html(r'<img src="{}" {} />', file.url, attrs)
|
||||||
|
return html
|
||||||
|
|
||||||
|
|
||||||
|
class Video(ZobrazitelnyBazmek):
|
||||||
|
...
|
||||||
|
|
||||||
|
class DummyBazmek(ZobrazitelnyBazmek):
|
||||||
|
def zobrazit(self, **kwargs):
|
||||||
|
return r'<p>Tohle zobrazit neumím :-(</p>'
|
||||||
|
def zmenseny_nahled(self, **kwargs):
|
||||||
|
return r'<p>Tohle zobrazit neumím :-(</p>'
|
||||||
|
|
||||||
|
|
||||||
|
def tipniTyp(soubor) -> DbObrazek.Typ: ...
|
Loading…
Reference in a new issue