RFC: Videjka a další bazmeky do galerií #99
|
@ -30,14 +30,14 @@ def prepnout_fotogalerii_do_org_rezimu(modeladmin, request, queryset):
|
|||
|
||||
class GalerieInline(admin.TabularInline):
|
||||
model = Obrazek
|
||||
fields = ['obrazek_velky', 'nazev', 'popis', 'obrazek_maly_tag', 'poradi']
|
||||
readonly_fields = ['nazev', 'obrazek_maly_tag']
|
||||
fields = ['soubor', 'nazev', 'popis', 'typ', 'poradi']
|
||||
readonly_fields = ['nazev']
|
||||
formfield_overrides = {
|
||||
models.TextField: {'widget': forms.TextInput},
|
||||
}
|
||||
|
||||
class ObrazekAdmin(admin.ModelAdmin):
|
||||
list_display = ('obrazek_velky', 'nazev', 'popis', 'obrazek_maly_tag', 'poradi')
|
||||
list_display = ('soubor', 'nazev', 'popis', 'poradi')
|
||||
search_fields = ['nazev','popis']
|
||||
|
||||
class GalerieAdmin(admin.ModelAdmin):
|
||||
|
|
33
galerie/migrations/0017_obecne_typy_souboru.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Generated by Django 4.2.20 on 2025-05-05 00:21
|
||||
|
||||
from django.db import migrations, models
|
||||
import galerie.models
|
||||
|
||||
def zatim_byly_jen_obrazky(apps, schema_editor):
|
||||
Obrazek = apps.get_model("galerie", "Obrazek")
|
||||
Obrazek.objects.all().update(typ='obrazek')
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('galerie', '0016_alter_obrazek_galerie'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='obrazek',
|
||||
old_name='obrazek_velky',
|
||||
new_name='soubor',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='obrazek',
|
||||
name='soubor',
|
||||
field=models.FileField(help_text='Lze vložit libovolně velký obrázek. Ideální je, aby alespoň jeden rozměr měl alespoň 500px.', upload_to=galerie.models.galerie_filename),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='obrazek',
|
||||
name='typ',
|
||||
field=models.CharField(choices=[('obrazek', 'Obrázek'), ('video', 'Video'), ('nevim', 'Neznámý typ')], default='nevim', max_length=16, verbose_name='Typ'),
|
||||
),
|
||||
migrations.RunPython(zatim_byly_jen_obrazky, migrations.RunPython.noop)
|
||||
|
||||
]
|
|
@ -24,10 +24,12 @@ def obrazek_filename_maly():
|
|||
pass
|
||||
def obrazek_filename_stredni():
|
||||
pass
|
||||
def obrazek_filename():
|
||||
pass
|
||||
def obrazek_filename_velky():
|
||||
pass
|
||||
|
||||
def obrazek_filename(self, filename):
|
||||
def galerie_filename(self, filename):
|
||||
gal = self.galerie
|
||||
cislo_gal = gal.pk
|
||||
|
||||
|
@ -44,43 +46,51 @@ def obrazek_filename(self, filename):
|
|||
|
||||
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):
|
||||
obrazek_velky = models.ImageField(upload_to=obrazek_filename,
|
||||
# „originál“ (modulo max. velikost uploadu na web FIXME!)
|
||||
ledoian
commented
doku doku
|
||||
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.")
|
||||
obrazek_stredni = ImageSpecField(source='obrazek_velky',
|
||||
processors=[Transpose(Transpose.AUTO), ResizeToFit(900, 675, upscale=False)],
|
||||
options={'quality': 95})
|
||||
obrazek_maly = ImageSpecField(source='obrazek_velky',
|
||||
processors=[Transpose(Transpose.AUTO), ResizeToFit(167, 167, upscale=False)],
|
||||
options={'quality': 95})
|
||||
class Typ(models.TextChoices):
|
||||
OBRAZEK = 'obrazek', 'Obrázek'
|
||||
VIDEO = 'video', 'Video'
|
||||
NEVIM = 'nevim', 'Neznámý typ'
|
||||
typ = models.CharField('Typ', max_length=16, blank=False, null=False, choices=Typ.choices, default=Typ.NEVIM)
|
||||
# Filename by default; slouží k řazení
|
||||
ledoian
commented
doku nebo help_text doku nebo help_text
|
||||
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)
|
||||
datum_vlozeni = models.DateTimeField('Datum vložení', auto_now_add=True)
|
||||
galerie = models.ForeignKey('Galerie', blank=True, null=True, on_delete=models.CASCADE)
|
||||
# Primární klíč k řazení pro overridování řazení podle názvu
|
||||
poradi = models.IntegerField('Pořadí', blank=True, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.obrazek_velky.name
|
||||
return f'Obrázek {self.nazev} ({self.soubor.name})'
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Obrázek'
|
||||
verbose_name_plural = 'Obrázky'
|
||||
ordering = ['nazev']
|
||||
|
||||
def obrazek_maly_tag(self):
|
||||
if not self.obrazek_maly:
|
||||
return ''
|
||||
return u'<img src="{}">'.format(self.obrazek_maly.url)
|
||||
obrazek_maly_tag.short_description = "Náhled"
|
||||
obrazek_maly_tag.allow_tags = True
|
||||
ordering = ['nazev'] #FIXME: zohlednit pořadí a asi i příslušnost ke galerii
|
||||
ledoian
commented
FIXME FIXME
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# obrázek potřebuje název, protože se z něj generuje cesta pro jeho uložení
|
||||
# (a pak se podle něj taky řadí)
|
||||
if self.nazev is None:
|
||||
self.nazev = os.path.basename(self.obrazek_velky.name)
|
||||
self.nazev = os.path.basename(self.soubor.name)
|
||||
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):
|
||||
nazev = models.CharField('Název', max_length=100)
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
/* velká fotka */
|
||||
/* zmenšování spolu s oknem prohlížeče */
|
||||
.galerie .obrazek, .titulni_obrazek {
|
||||
max-width: 100%;
|
||||
max-width: max(100%, 900px);
|
||||
zelvuska marked this conversation as resolved
zelvuska
commented
900 je nějak moc, ne? (Třeba když jsi na mobilu, tak to znamená, že uvidíš minimum z toho obrázku, ne?) 900 je nějak moc, ne? (Třeba když jsi na mobilu, tak to znamená, že uvidíš minimum z toho obrázku, ne?)
ledoian
commented
To je vzaté z původních To je vzaté z původních `views.py`, za to nemůžu já :-D Ale je to hodně, co třeba `600px`? (Haluzím velmi náhodně a na monitoru, zas to nemůže být moc málo, jinak se to zmenší moc…)
zelvuska
commented
Aha, tak to asi nechme… Aha, tak to asi nechme…
|
||||
max-height: 900px;
|
||||
height: auto;
|
||||
width: auto\9; /* ie8 */
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.predchozi_obrazek{
|
||||
|
@ -75,6 +76,8 @@
|
|||
|
||||
.galerie_nahledy img {
|
||||
margin: 10px;
|
||||
max-height: 100px;
|
||||
max-width: 200px;
|
||||
ledoian
commented
Hodnoty random nastřelené… (asi nevadí) Hodnoty random nastřelené… (asi nevadí)
|
||||
}
|
||||
|
||||
.galerie_nahledy div.navigace {
|
||||
|
|
38
galerie/static/galerie/neznamy_placeholder.svg
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="768"
|
||||
height="576"
|
||||
viewBox="0 0 768 576"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
id="layer1">
|
||||
<path
|
||||
style="fill:#6f2509;stroke:#6f2509;stroke-width:24.4876;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;paint-order:markers fill stroke;fill-opacity:1"
|
||||
id="path1"
|
||||
d="m 171.56486,156.34958 277.81099,481.18278 -555.62201,-2e-5 z"
|
||||
transform="translate(212.43515,-108.94158)" />
|
||||
<g
|
||||
id="g4"
|
||||
style="fill:#ff0000"
|
||||
transform="matrix(0.63613865,0,0,0.62847938,383.26722,188.1938)">
|
||||
<circle
|
||||
style="fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:32.6502;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
id="path2"
|
||||
cx="0"
|
||||
cy="430.19247"
|
||||
r="64.870293" />
|
||||
<path
|
||||
style="fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:24.4876;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;paint-order:markers fill stroke"
|
||||
id="path3"
|
||||
d="m -93.037653,283.56151 -174.608127,-302.430127 349.216236,-10e-6 z"
|
||||
transform="matrix(0.69829007,0,0,1.0128734,64.967275,29.408685)" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
61
galerie/static/galerie/video_placeholder.svg
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="768"
|
||||
height="576"
|
||||
viewBox="0 0 768 576"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="video_placeholder.svg"
|
||||
inkscape:version="1.4.1 (93de688d07, 2025-03-30)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="1.1715686"
|
||||
inkscape:cx="377.69874"
|
||||
inkscape:cy="285.08787"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1164"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg1" />
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
id="layer2"
|
||||
style="stroke:#aaaaaa;stroke-opacity:1;fill:#cccccc;fill-opacity:1">
|
||||
<g
|
||||
id="rect1"
|
||||
style="opacity:1;fill:#cccccc;fill-opacity:1"
|
||||
transform="matrix(0.95921938,0,0,0.86504219,15.659757,38.867849)">
|
||||
<path
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;vector-effect:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;paint-order:markers fill stroke;enable-background:accumulate;stop-color:#000000;stop-opacity:1;fill:#cccccc;fill-opacity:1"
|
||||
d="M 0,-12.244141 C -6.7620501,-12.243653 -12.243653,-6.7620501 -12.244141,0 v 576 c 4.89e-4,6.76205 5.4820913,12.24365 12.244141,12.24414 h 768 c 6.76205,-4.9e-4 12.24365,-5.48209 12.24414,-12.24414 V 0 C 780.24365,-6.7620497 774.76205,-12.243652 768,-12.244141 Z"
|
||||
id="path4" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="layer1">
|
||||
<g
|
||||
id="path1"
|
||||
style="opacity:1;fill:#ff0000;fill-opacity:0.771591"
|
||||
transform="matrix(0.77572769,0,0,0.77572769,169.75532,213.5323)">
|
||||
<path
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;vector-effect:none;fill:#f5b34f;fill-opacity:1;stroke-linecap:round;stroke-linejoin:round;paint-order:markers fill stroke;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||
d="m 54.501953,-169.58203 c -5.851459,1.03154 -10.117969,6.11495 -10.119141,12.05664 v 507.05078 c 8.82e-4,9.42554 10.204294,15.31604 18.367188,10.60352 L 501.86719,106.60352 c 8.16111,-4.71353 8.16111,-16.493503 0,-21.207036 L 62.75,-168.12891 c -2.493096,-1.43876 -5.413362,-1.95325 -8.248047,-1.45312 z"
|
||||
id="path3" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -1,5 +1,6 @@
|
|||
{% extends "galerie/base.html" %}
|
||||
|
||||
{% load bazmeky %}
|
||||
|
||||
{% block nadpis1a %}
|
||||
{{galerie.nazev}}: {{ obrazek.popis | default:"Fotka" }}
|
||||
|
@ -64,11 +65,7 @@
|
|||
{% endwith %}
|
||||
{% endif%}
|
||||
<span id="nahoru" class="kotva_obrazku"></span>
|
||||
<img src="{{obrazek.obrazek_stredni.url}}"
|
||||
height="{{vyska}}"
|
||||
width="{{sirka}}"
|
||||
alt="{{obrazek.popis}}"
|
||||
class="obrazek">
|
||||
{% zobrazit obrazek.jako_bazmek alt=obrazek.popis title=obrazek.popis class="obrazek" %}
|
||||
|
||||
{% if obrazky_dalsi %}
|
||||
{% with obrazky_dalsi|first as dalsi_obrazek %}
|
||||
|
@ -79,7 +76,7 @@
|
|||
{% endif%}
|
||||
</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>-->
|
||||
|
||||
{# Popisek fotky #}
|
||||
|
@ -110,21 +107,14 @@
|
|||
{% endif %}
|
||||
{# nahledy predchozich obrazku #}
|
||||
{% 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>
|
||||
ledoian
commented
zrušit height? zrušit height?
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<img src={{obrazek.obrazek_maly.url}}
|
||||
height="{{obrazek.obrazek_maly.height}}"
|
||||
width="{{obrazek.obrazek_maly.width}}"
|
||||
alt="{{obrazek.popis}}"
|
||||
class="obrazek"
|
||||
id="prostredni">
|
||||
{% zmenseny_nahled obrazek.jako_bazmek alt=obrazek.popis class="obrazek" id="prostredni" %}
|
||||
|
||||
<div class="navigace">
|
||||
{# nahledy nasledujicich obrazku #}
|
||||
{% 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>
|
||||
ledoian
commented
i tady zrušit height? i tady zrušit height?
|
||||
{% endfor %}
|
||||
{# odkaz na nasledujici galerii #}
|
||||
{% if nasledujici_galerie %}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
{% extends "galerie/base.html" %}
|
||||
|
||||
{% load bazmeky %}
|
||||
|
||||
{% block nadpis1a %}
|
||||
Galerie {{galerie.nazev}}
|
||||
{% endblock %}
|
||||
|
@ -26,7 +28,7 @@ Galerie {{galerie.nazev}}
|
|||
{% if not obrazky %}
|
||||
<div class="galerie_hlavicka">
|
||||
{% 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 %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -55,10 +57,7 @@ Galerie {{galerie.nazev}}
|
|||
{% endif %}
|
||||
class="podgalerie_nahled {% if pgalerie.zobrazit == 1 or pgalerie.zobrazit == 2 %}mam-org-only{% endif %}{% if pgalerie.zobrazit == 3 %}mam-resitel-only{% endif %}">
|
||||
{% if pgalerie.titulni_obrazek %}
|
||||
{% with pgalerie.titulni_obrazek.obrazek_maly as obrazek %}
|
||||
<img src="{{ obrazek.url }}"
|
||||
/>
|
||||
{% endwith %}
|
||||
{% zmenseny_nahled pgalerie.titulni_obrazek.jako_bazmek class="" %}
|
||||
ledoian
commented
Má mít class nebo tam ten atribut nemá být vůbec Má mít class nebo tam ten atribut nemá být vůbec
|
||||
{% endif %}
|
||||
<div class="nazev_galerie">
|
||||
{{ pgalerie|truncatechars:max_delka_nazvu }}
|
||||
|
@ -96,13 +95,10 @@ Galerie {{galerie.nazev}}
|
|||
{% if obrazek.popis %}
|
||||
title="{{ obrazek.popis }}"
|
||||
{% endif %}
|
||||
href="./{{obrazek.pk}}#nahoru" class="galerie_nahled"><span class="vystredeno"></span><img
|
||||
src="{{obrazek.obrazek_maly.url}}"
|
||||
{% if obrazek.popis %}
|
||||
title="{{ obrazek.popis }}"
|
||||
{% endif %}
|
||||
width="{{ obrazek.obrazek_maly.width }}"
|
||||
height="{{ obrazek.obrazek_maly.height }}" />
|
||||
href="./{{obrazek.pk}}#nahoru" class="galerie_nahled">
|
||||
<span class="vystredeno"></span>
|
||||
{% zmenseny_nahled obrazek.jako_bazmek %}
|
||||
{# title=obrazek.popis (a byl tu if, že se použil jen když existoval…) width=obrazek.obrazek_maly.width height=obrazek.obrazek_maly.height #}
|
||||
ledoian
commented
smazat? title by se měl použít z vnějšího tagu, či? smazat? title by se měl použít z vnějšího tagu, či?
|
||||
</a>
|
||||
{% endfor %}
|
||||
<br>
|
||||
|
|
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)
|
140
galerie/typy.py
Normal file
|
@ -0,0 +1,140 @@
|
|||
"""
|
||||
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
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
from django.contrib.staticfiles.finders import find
|
||||
# FIXME: static fily na lokálním webu??
|
||||
find = lambda x: '/static/'+x
|
||||
ledoian
commented
FIXME FIXME
|
||||
from django.utils.safestring import mark_safe, SafeString
|
||||
from django.utils.html import format_html, format_html_join
|
||||
from django.urls import reverse
|
||||
|
||||
from imagekit import ImageSpec
|
||||
from imagekit.cachefiles import ImageCacheFile
|
||||
from imagekit.processors import ResizeToFit, Transpose
|
||||
|
||||
HTML = str | SafeString
|
||||
ledoian
commented
Komentář, že v Py3.12 má být Komentář, že v Py3.12 má být `type HTML = ...`
|
||||
|
||||
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}
|
||||
|
||||
def _fmt_attrs(attrs):
|
||||
return format_html_join(' ', r'{}="{}"', ((mark_safe(k), v) for k, v in attrs.items()))
|
||||
|
||||
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 = _fmt_attrs(kwargs)
|
||||
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 = _fmt_attrs(kwargs)
|
||||
html = format_html(r'<img src="{}" {} />', file.url, attrs)
|
||||
return html
|
||||
|
||||
|
||||
class Video(ZobrazitelnyBazmek):
|
||||
def __init__(self, *a, **kwa):
|
||||
super().__init__(*a, **kwa)
|
||||
self.placeholder = find('galerie/video_placeholder.svg')
|
||||
|
||||
def zobrazit(self, **kwargs):
|
||||
attrs = _fmt_attrs(kwargs)
|
||||
# Atributy specifické pro video musíme vesměs vyřešit tady… (šlo by to {% if %}-ovat podle typu, ale to je spíš haluz.
|
||||
html = format_html(r'<video src="{}" preload="metadata" controls {} />', self.soubor.url, attrs)
|
||||
return html
|
||||
def zmenseny_nahled(self, **kwargs):
|
||||
attrs = _fmt_attrs(kwargs)
|
||||
return format_html(r'<img src="{}" {} />', self.placeholder, attrs)
|
||||
|
||||
class DummyBazmek(ZobrazitelnyBazmek):
|
||||
def __init__(self, *a, **kwa):
|
||||
super().__init__(*a, **kwa)
|
||||
self.placeholder = find('galerie/neznamy_placeholder.svg')
|
||||
|
||||
def zobrazit(self, **kwargs):
|
||||
attrs = _fmt_attrs(kwargs)
|
||||
# Stavíme HTML ad-hoc, co se může rozbít :'-)
|
||||
obrazek = format_html(r'<img src="{}"/>', self.placeholder, attrs)
|
||||
popisek = format_html(r'<p style="text-align: center; font-size: 1.1em;">Prosím oprav typ aktuálního obrázku v <a href="{}">adminu</a>!</p>', reverse('admin:galerie_obrazek_change', args=(self.db_objekt.id,)))
|
||||
ledoian
commented
hnus hnus
|
||||
return format_html(r'<div {}>{}{}</div>', attrs, obrazek, popisek)
|
||||
def zmenseny_nahled(self, **kwargs):
|
||||
attrs = _fmt_attrs(kwargs)
|
||||
return format_html(r'<img src="{}" {} />', self.placeholder, attrs)
|
||||
|
||||
def tipniTyp(soubor) -> DbObrazek.Typ:
|
||||
from PIL import Image, UnidentifiedImageError
|
||||
try:
|
||||
Image.open(soubor)
|
||||
return DbObrazek.Typ.OBRAZEK
|
||||
except UnidentifiedImageError:
|
||||
return DbObrazek.Typ.NEVIM
|
||||
logger.warning("Nepodařilo se tipnout typ nečekaným způsobem!")
|
||||
return DbObrazek.Typ.NEVIM
|
||||
|
|
@ -89,10 +89,6 @@ def nahled(request, pk, soustredeni):
|
|||
|
||||
def detail(request, pk, fotka, soustredeni):
|
||||
"""Zobrazeni nahledu fotky s id 'fotka'."""
|
||||
MAX_VYSKA = 900
|
||||
MAX_SIRKA = 900
|
||||
MAX_VYSKA_MALA = 100
|
||||
MAX_SIRKA_MALA = 200
|
||||
NAHLEDU = 1
|
||||
|
||||
galerie = get_object_or_404(Galerie, pk=pk)
|
||||
|
@ -141,23 +137,11 @@ def detail(request, pk, fotka, soustredeni):
|
|||
else:
|
||||
form = KomentarForm({'komentar': obrazek.popis})
|
||||
|
||||
# Preskalovani obrazku do vybraneho prostoru.
|
||||
vyska = obrazek.obrazek_stredni.height
|
||||
sirka = obrazek.obrazek_stredni.width
|
||||
if vyska > MAX_VYSKA:
|
||||
sirka = sirka * MAX_VYSKA / vyska
|
||||
vyska = MAX_VYSKA
|
||||
if sirka > MAX_SIRKA:
|
||||
vyska = vyska * MAX_SIRKA / sirka
|
||||
sirka = MAX_SIRKA
|
||||
|
||||
return render(request, 'galerie/Galerie.html',
|
||||
{'galerie' : galerie,
|
||||
'predchozi_galerie' : predchozi_galerie,
|
||||
'nasledujici_galerie' : nasledujici_galerie,
|
||||
'obrazek' : obrazek,
|
||||
'vyska' : vyska,
|
||||
'sirka' : sirka,
|
||||
'obrazky_predchozi' : predchozi_obrazky,
|
||||
'obrazky_dalsi' : nasledujici_obrazky,
|
||||
'upravy_popisku' : dovolit_upravy_popisku(galerie, request),
|
||||
|
@ -200,9 +184,11 @@ def new_galerie(request, galerie, soustredeni):
|
|||
gal.save()
|
||||
|
||||
# zpracovani obrazku v galerii
|
||||
from galerie.typy import tipniTyp
|
||||
for obr in request.FILES.getlist('obr'):
|
||||
o = Obrazek()
|
||||
o.obrazek_velky = obr
|
||||
o.soubor = obr
|
||||
o.typ = tipniTyp(obr)
|
||||
o.nazev = str(obr)
|
||||
o.galerie = gal
|
||||
o.save()
|
||||
|
|
Možná by směrem zpátky spíš mělo selhat, pokud v galerii existuje ne-obrázek…