From 1d1c80a2679065b8bf2d598ab8d5c556002eed84 Mon Sep 17 00:00:00 2001 From: "Adrian A. Baumann" Date: Mon, 29 Dec 2025 19:52:22 +0100 Subject: [PATCH] Image handling refined (filenames, mobile rendering) --- argocd/deployment.yaml | 4 +-- boxes/migrations/0004_alter_thing_picture.py | 19 +++++++++++ boxes/models.py | 33 ++++++++++++++++++- boxes/templates/boxes/thing_detail.html | 4 +-- data-loader/preload.sqlite3 | Bin 184320 -> 196608 bytes 5 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 boxes/migrations/0004_alter_thing_picture.py diff --git a/argocd/deployment.yaml b/argocd/deployment.yaml index 5c3ff96..d931d6a 100644 --- a/argocd/deployment.yaml +++ b/argocd/deployment.yaml @@ -18,7 +18,7 @@ spec: fsGroupChangePolicy: "OnRootMismatch" initContainers: - name: loader - image: git.baumann.gr/adebaumann/labhelper-data-loader:0.010 + image: git.baumann.gr/adebaumann/labhelper-data-loader:0.011 securityContext: runAsUser: 0 command: [ "sh","-c","if [ ! -f /data/db.sqlite3 ] || [ ! -s /data/db.sqlite3 ]; then cp preload/preload.sqlite3 /data/db.sqlite3 && echo 'Database copied from preload'; else echo 'Existing database preserved'; fi" ] @@ -27,7 +27,7 @@ spec: mountPath: /data containers: - name: web - image: git.baumann.gr/adebaumann/labhelper:0.032 + image: git.baumann.gr/adebaumann/labhelper:0.033 imagePullPolicy: Always ports: - containerPort: 8000 diff --git a/boxes/migrations/0004_alter_thing_picture.py b/boxes/migrations/0004_alter_thing_picture.py new file mode 100644 index 0000000..092fefe --- /dev/null +++ b/boxes/migrations/0004_alter_thing_picture.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.9 on 2025-12-29 18:26 + +import boxes.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('boxes', '0003_convert_thingtype_to_mptt'), + ] + + operations = [ + migrations.AlterField( + model_name='thing', + name='picture', + field=models.ImageField(blank=True, upload_to=boxes.models.thing_picture_upload_path), + ), + ] diff --git a/boxes/models.py b/boxes/models.py index 59950bb..df95989 100644 --- a/boxes/models.py +++ b/boxes/models.py @@ -1,7 +1,19 @@ +import os from django.db import models +from django.utils.text import slugify from mptt.models import MPTTModel, TreeForeignKey +def thing_picture_upload_path(instance, filename): + """Generate a custom path for thing pictures in format: -.""" + extension = os.path.splitext(filename)[1] + safe_name = slugify(instance.name) + if instance.pk: + return f'things/{instance.pk}-{safe_name}{extension}' + else: + return f'things/temp-{safe_name}{extension}' + + class BoxType(models.Model): """A type of storage box with specific dimensions.""" @@ -72,10 +84,29 @@ class Thing(models.Model): related_name='things' ) description = models.TextField(blank=True) - picture = models.ImageField(upload_to='things/', blank=True) + picture = models.ImageField(upload_to=thing_picture_upload_path, blank=True) class Meta: ordering = ['name'] + def save(self, *args, **kwargs): + """Override save to rename picture file after instance gets a pk.""" + if self.picture and not self.pk: + picture = self.picture + super().save(*args, **kwargs) + new_path = thing_picture_upload_path(self, picture.name) + if picture.name != new_path: + try: + old_path = self.picture.path + if os.path.exists(old_path): + new_full_path = os.path.join(os.path.dirname(old_path), os.path.basename(new_path)) + os.rename(old_path, new_full_path) + self.picture.name = new_path + super().save(update_fields=['picture']) + except (AttributeError, FileNotFoundError): + pass + else: + super().save(*args, **kwargs) + def __str__(self): return self.name diff --git a/boxes/templates/boxes/thing_detail.html b/boxes/templates/boxes/thing_detail.html index 20fdfd3..733a9ed 100644 --- a/boxes/templates/boxes/thing_detail.html +++ b/boxes/templates/boxes/thing_detail.html @@ -20,10 +20,10 @@
{% if thing.picture %} {% thumbnail thing.picture "400x400" crop="center" as thumb %} - {{ thing.name }} + {{ thing.name }} {% endthumbnail %} {% else %} -
+
No image diff --git a/data-loader/preload.sqlite3 b/data-loader/preload.sqlite3 index fa0e717df173cf676c79ac5ee4c1ad34e74e5eba..2c608a05f93c6efeb10f6c32605f07d2623dd8f2 100644 GIT binary patch delta 9759 zcmb_i33MDsneOiC(P*wN9hPKWc3ZL{%a%rcA0s=CEXkK_OSXKFbsVQ>rbilSq!~|- zZ21Tig>$p8IB{TJ2yZvAT)U73VSUIXQ3Cw$g8wx9zy02BnSOTRj`t!@L!kSd zud0U$bT7>R&VTd91~1jHsXvyPvmHSYl%rNUZ|9EYCsH%xN3*GTel}-If~59{QjcWv zq7jvZs3JrJLsLxU{AAO+y!GoU(9h5v=rMF0{SA62T8H}32T{$!d*4fCjm|4V=BXiqPl zn6zfbGe@UW<2fsz%FH+o9}^zkTusy!rKoB|G*MJd^ZZvbQJ(XnJcWLMzKH$_y$$6T z=3}VP=&hi-7@8TL9Uo6Qyrj*^>YPEdqx9{K?2<0gn1fz}`uHf#HhU_l4GdG}lJz-N znUh31M?K55G%>jIvPfQ(=YZFs?jEH+-b^oHlsd_HqDr&WJLucPFhf83EQS7veuI92 zeu!Q`-$GvpeLs7?I{N`+vVITp>ue>UR^jth(`t;VsKBkoc)cD(E8YqgewAZvg$K75 z%Q8SLo&V!U0>6q3bDJsjC-iP~D;h()(Kb}gokqV!4PBe!{)PJF#7s225up5HWR#=yxBx>Bf&m`QGzBxgP=}OE09Jds1TG1 zN(4oM0>NH_5rRD!E4vA9Ah@1j7e?PY^4v-ACW0LV*Ai?e*oM)+#@mV;9{=K7O>h;# zR)Q^fiQeZK=I$x_K1%C%pjnhfQz(fnbQ?N^_JQ%XqXBd?GLZtNTaP*rkD5_EszxE? zLk#yS_Xn`bE8NexA962p&w*W@;l9d!f%`1?I9TRm+{4@lxqsmP4s3Hj_crdHh528E z3#`Xa4`bMgVF!lo7`9>9ieU)DAcg@9TQKxvxCO&z3^!xggkd9wJ`7O|CI$n8jzPnq zVo)&1tf%0oCHyF25HR#&h+yc!(2ZdOhV>Y_Fs#GSiQy&;9T?VPXvffoVGRZz!)gqx zFtlQ5!O)DM3B!dqe}OS2~SnMAAJ!=&~MQz z=qKnU^c?zE9G(!I52G<0f4U!|R!L$q=5s@{q7<3sz{GLs!S5u7h2RteB?8(TnK2=-23F^dk`b2qJ6k$0}B0=U{vUK`2doA?RtEWeIvP3FGnj{Wy(y16wKX zx$q;Qv%#sr)`fW`uQi@N;Vv?Gb9mqfSZ0wkkSM|9W(=?`&`Qi($ z%=v<~eR4XgP2`S=bCaoUBcqeq!BcTlJ$B+XrxJr(CU@J1gq`MaVw*X5>!I0QCvSzV zLhBJk*e&o@QOdoNXzJSCZ7dnr7*FWA+^J(JDK1WBbjzMTc08SyopDRjbLlxTV(*+g zIJtXhgZT*q(?qFHYSY6a=VXChc$+|$2E2|Sl$rT zgrFMs)k^HNgvn&|Q2*dDYv1va!LdXtbFBZQI6X3SU|2}zoN4>eK_~N9<9l`O(7=I_ z%rYeeIjYDJQ8!FUyYNUWGs3_&w4Fj1(0ync!uZm{yoL&EJ-g`}*~N>AZnTIjCc4oi zJ(x&|rB3CXtUh6-lf^H$GAoMD_cC3eT#`cHN1sRUMQ=dKh57x+TIK1d7p?3v^Lm$c zFI#x_>h8r$;=$d;Csr{{#g{fS?OG#Bd(iXf+px)e4Sf&&h-`(gpf8|{=;P?0p}IJW zUXNaj=1>m32R(t(=w0X?2ojitNJg({B(boPU<1K=f^`^uE68&#d9EQ?jnP|0-Xnq> z!7#xP#)=?$4&eB5;}AdjFu-n0`x)HLcz5HdgQs#hl4k}7cO%NV!Tl0?5`6&l{xJmi zm(iy|b9XMkMxR6~v z*RURnuEs!S6uN3gObB<92{8ezVFhF?K$2Q2U``aClOHk*o)b&fl!eSf;mkt$@oYry zDYrm*1^pBpR?}ke79C{15&uh^M*tk+Ch7P0sq~d^bVo9}8 zU8*9og*P4GKnF;yu$@C z!cyf}huf@mn|8NpbDK4!v3R#x?KZ32rqyj)+@{%Wn%t(*ZC1KXgWJ@*O##dddj*&o z5X=k+t_ujR3kZfrv^hrnj{qrXLOMt7q# zP|au2Bub#$(QDAYb!Zpbif%zss5K%`;R03EI#Tu2k*a4!3w~2e+`N{AWeus1YKVhZ zk#I#|K$=EiKtNKGAyRH3*WG)(1?UKo$}B``vJi=j5Q*s!ar+=C0E6HaG#v!5032df06R&gQB8%p6%-qKghIpIsn8?g{tH!SnfHYX^L~$a-aFswWOC`A{KV{ZY{p8Z z&z!mZ=$ttdlasolsCrV+3@xr`Mlx>4G{Z7uF*6xA?Za)d4xM6Bj>k+@5aVJZu8U$q zmz4z2Nut))&9|wRq8qlLCv3x%lWIZ}%|x~pA1lo7VPJ0Zb>?=>#aXmA%jcGi^~S~CWGpVmZ8a{(tt70tDwvX@Dr!toY%6ZZ zrA1RK{N?7R!qill*}O!vr{_;!XGY7Mrf`+lT$vI%vOB%R%!^e1%W13#@_^LM%YvjF zxigHuL^X!rPQ&fOGw4ppPoF^ja3tTny+yex%YE#Lua`&oW{}Ibg3snsGjO}XXRTb`39ek}_IA+q4eV&<1avsF*=+hG z)6!7pTiM9&v!K@F%JVKYiik{G>FP&rtSEIHIs^4>BYk~ z*0_|NH0$+^Po5-CfZC@_zqgRRA$zxLzPqkYtsrvNd-H00?OP8 z+X>Zhu96Kj6Xw`GskxNRR)yVnwKRjO*n?XK+4@jv^#h&#WDjqE18EYD%WDrS?zJ>P)9St5W8z=&|$ekmCH~^ zG2Y=+-dVxA8n)7bYIZQ?WYgA3he2NVZJnR-Fx%3zHcrQt7@C;!Y@5oa0js#)Q{!_d z&Rle?GOzaspp%Gz)w^nh=Z@0nmYa9@D-zGq>+(RX3f z1?EFc@vg_2CWad+zWgX-((8)zpMmPzyvX#^dy1zoG7{bCzWUrnMxl2Xe}9o_qlIGs zCz-~g{1}rhKKU5)Sn;99m^F0Qg$EvIraS=_1t}<{{u(`XfqQ~^l`HglPSe+k`5R?u zwx6B%Ud8R}DAFY_K(7dmDpY*=fI{+c_CH74$TzYsu=XtYdmnY0T%iPb5 z(y!8RAwcWYw?W>OpMlMOs=CX+f%f;y#uh7=v(pfX@hOPt!>8M#)D>Bj_05|=)Ec6YBwfi##d|N&jS-W_1FKP4i-@q1n(EL-pzd+0MMzXj@)Y6e zaCyY2AV+lB&{TP2AMiB0JVGgZ5z?qjDnw;`z?F1W*&7AYAwnvee=esH{tbj*FkxvT zf~X6+w#5YgPM6ad}QAJZsB5U`tUkBtz*Ffv3?`L?Ef!U51h_Miiv0k_e=m1wVz_38lFx ze-pXbM?e?2<^j!BK{vIHy+G|Qno-W`i&Ze9f*3I*$rQD|2#~t&q?ht1p%h)AdQ?@w zn>3KSy$2{&qJnmNVBOD(?hdAg}I9K^C2F~DP0QSG`soT3mx@v&6JN^(RI6$y0dysV2Hlb2|$Vr6tm5F|mr=?ax4 zFV+~6fF4n0Rf7t&vfM5VJGGt*&6 zU83lzT&l1r;2Spu%63`rYQw4!LdlY>>ypB7-D`lf!IdeH)aUWLf(zu^6D$Q}3002B znrUi!l*i=6c%na7|HgKYn2a3>?k| zgr1=dtO6k$hyrp+Ncj`cHKkk;0^Th`PLN?K2U;%+kxD|!pNg&s=_6aoGPe-4sH}p) zO<0U{3zkBk#^xwValc`SYVqkyaKF6=PFqFqCAu9xis5N!Q9**lZfd4_;hyi($18Ze zX@)|#QSd$I4(?anTeuOfF8plx!SMdj??T1UZ42`oLx;>^So0pb8pcsgd2ne#%^Zf^ z?#d2@F9>BkQA5T@og+I5>#f&f#k*Z=@gM(E@5kLM44_*+PQT%QD=hM+`Wg%SLQ~FypG>TFk`W2mV~;;EIC~H$j>< zvjxQMAVr8G?n=$r7MzbVc{u5$;2?tQa(-RXN3$t#hvNCiFs8_P3y!WC@HvZi2H z7X1?xd^&v^?u*|DH|#r64_XNqRWHH^$=7iQxCrM9e=q!a_?_Wn;gPTs=0Y!rz7Tpa zG#lCzQbSe2mxG@RzBl;BU@ABqY!3V;@MPcv0Vi-cFcetff7Sm}|Cjs^`_I7;@U!rS zKUNmH`g~MR7|}h;gFM6CZZo}8Uff)T+-9U`5hWBcLMJ3bD>Ii({le>^o)$ir2{m_@=HU8i&Yp%ia`^r&1A|IK%Z3u~XaN`4o)BtgYF$qo+c)^_l4{kI%umz0lfYYXJIp$fr7LS6R<}w|RJ$4Ebib1%#;4c)q z+I`gUGAg*G83##aAHnx@4v@CD`KZmyNT;oQE)};64linRP;bQ8HEVoS-?gb69{vCW zNY?Tt%}PfFo=lmK!D*m;r6OB3jjM5-g8oZSW{zyt^Q(N6iM#e$PBv!ea)p!pXo{D4 z61e;p*{bVo^-ck{W->`VeKF5}a(RbV@sN>*7)1e?b9?|K0K z@dx&dz#oEiG%hC_0sCPWH|8{jKQWQY+n8)+i43v{YzcWD?@`@++JZ|YFb<|aC|kAN z4L)iw)~ulSjM?MU;MOF4;Cq#BJ^>fhaQg^v#$jbaYIl8!{QA_&BI>#uQp;uPg2lG3 z@KGxE*RoL;n^US;SzF4wyCl=t&TsFDc@UQ?_lTj!aA`{zwPxUs!QGHyQ4flN+E7XF;e)$7O4&;E aHwArEFV=0lZRMQ_2&E~U_rYjl=>Gtw-rKtX delta 1044 zcmZ8gZD?Cn7(VAc_a?b%?|qVN?KH79TIU?DOB-j`W|lJC7`Tcc{t<^=>|#vUC9z4X zEesO`!TH0~st2b(Dkv(VGlqI?y4gYz{6JQ5C_47TkG0eNu(Ga$so=>iYW00NFP!t7 z=RD7Q4yRD4FHF{zLjD7cu~s$yj{C>A-Np};555M`=pC4F`&!Gz4sT^3_=?`ROUKVR zh#8FF863x(ID=i-SuS3WO&bE@R{l`t@kFjK|41sG%#UUhI%5;}Mp}4iVk8m``0B(K zV{M13HGHBP32uWaTGkVdnvn)BBK*)mJe|z+9Z4mJ;`vl2oojr*ZMw(b8E)^2wROhY zy0%Ae@7USiUimB&3%fX$7?vvj>^W@S#9gd~i1rUBlc`)d+R=Z6Yy3g>qN&a%Tw=J2 zOBH`%32vQ)--yO5S5J7CV^uFFFw_@oG7OuFCW>T2$QJvPgq^0{l$A>){IFOXX9HSA;G z(uj+DD|}A)JcN~+@`^Hb0oS;MpGV`Q2^j5O$MO}1KX65%7tx>zgOd=3;dEn8G3z^X zs$6`_w5OWr9&!Z)vL&0;r~bIlZ# z-}g|9rQ>`}={#1kj9=82uh3f4({;hQ(3~oq>Nu#G)3NB7!y+!?H$^#v9sf|sp=d%A ztmc_Ho|%s^Jn;Wi%`M`&WlsR}`1HT|FSLO5xr)^_XwKuQ`+6;HUx{9?FPHX(>F#Fu0(V*C)_vwF?}~TSxZv4fZm)mI-R)i|7ej8l+orck6s?%ls?^1| z%Hk|-l#NxoPNu80nRm(Os}$w8tgWA|(hlA!eKT|;-y(N^NY~2V8OqABlhiDyXXqoj dG)*D<_(>WN-r6Hrr>)c2XIpdhr!`)>{1*yE8omGk