From a62aed56ae047294bd9562c2a8a7731b47bab264 Mon Sep 17 00:00:00 2001 From: Martin Putzlocher Date: Wed, 20 May 2026 01:02:29 +0200 Subject: [PATCH] Versuche die Eltern-Kind-Beziehung zu bauen, Fehler beim Erstellen der Migration. --- mensa_app/__pycache__/admin.cpython-314.pyc | Bin 1807 -> 3996 bytes mensa_app/__pycache__/models.cpython-314.pyc | Bin 16670 -> 18536 bytes mensa_app/admin.py | 43 +++++++++++++++- mensa_app/models.py | 49 ++++++++++++++++++- 4 files changed, 89 insertions(+), 3 deletions(-) diff --git a/mensa_app/__pycache__/admin.cpython-314.pyc b/mensa_app/__pycache__/admin.cpython-314.pyc index 8bd90d787b0cfae25c1946611bbb22ab40e06bc5..5d48f816388537ceb1508b61c71a3b0253c3defb 100644 GIT binary patch literal 3996 zcmb7G-A^0Y6~8n78k?~V7y|iN7(>3SWX-l=mo#juEGdC3Az>$xv#LTSgMG0l3}bTd zbrYgKxYcUYJS}}{-nx}4wR!7P)k-V1QvLzlAc01$6sgTa)fZZb(!TYanSsTLS1lv? z_}+8RoH?Jrb5Es`2?Ed0|DoO$Dj|Qx&CU}I7F*jgA$4+_=+b2}DG8~c+?I6tvOF0Q zp?=a$vP2K{5IsCA%_uuBfBiok^4B5)=kSd;pDdXT4NE@P(-i?-QIJi&DM`qVuHoQF zC}2qpEQyPFKY@3;-hO8$`8IDhzVk)T7X>u5iMD-dXlv3C2MwxF_oYE?(vScRNs;vI zX@hl2r1qsP)uc@Y4QY|ymxgqch9qcc7w!Af(B7mW1sXa;$G$Xle67K&g*4Pc`~Rq` z&Tp)%B^R|n`q+5e}mGJ0l^hwx_- zQ>v3$A|#y%`F2!<4v?y(OCmfQX;uBLtPzSXi?|*ViQ7c9>0zPjiVj_2OCnhyXCyre zYel5=m`E2S(Ow{0*(FAu+C4BV;q$clXJ0X`WOD5iwM*Ka zJHRK^97{E!Yj;<_Ce)$%}O7dOE+F&ad#FE&p`+%TF6U`Bf#q1&5@#H#8Aov=O3i z{T3DkK8 z+tI&ePyDLy7k%s5kw$jpcf)@;|NHanqf?F1snzVr^X!$MTv|ETQ2YLz?s|OxPwAoG zD2?=$KNf(jDpwc|zNh4=bT!xMDu#ivF$_0h7>m_X&BnEA824(X9h{^MaFlU@IN?;` zIA$27YSA$Kh$=g<;KoFSRcqyn8v#w!cGVwHHdp1;`-Gd>yEB&T8Z&J-LLUfby0Q5M zPl$DbZGNP>A%embR?#qpVAfnsPzb8q5yZD-6-ANSHn5Tb%ilhgnFdOBxhM##u;4FT z@H2t86@~K3N)4a6S^5I-Wqx~xliSw!@xh|b3H z65x8w!*94P*$SX}9I!nB2;Sx)xC$+!xNUwheS`xC3J}YVx~Uqc%rKB*{+f^iI{*w) zw+3K=NO5b|&g&uQOsLqKox=e(AY|46F-YRtk^l@d?Q>eo{@fh}9A^QvY?;EJkNHps zFhVSg$pWPD%4^rRKbqd5dLM!yqwnEywAK!1JPyhPn^Ypn@<(!{63<(Yd3>?FIAR9q~ z(8UJvEXj`I`WO;KHHP!YRc(t4qh#?V+gx^&oSLjy@qvN63iwQ!<&?d;aATG?YdJ%+ z*?UO7h2+~v&H!nu(hrb}1TEw50`aQ!MJ5mR*nRBF2diE0)u%Vo2kVoYQPO^NHFdN; z@j4uijJ_s7e6R3*0K^33sAk{CAqKnT^(AU?3Klyz%rY?IQ>LKhDzor3HUTEXWQJt~ zW?T(NrK)2u2Wjvljhg(Jcxe1F zZjpQfKaQ-=$%fhk{fX>B!?{cOT_10}BJ zy4+OhE;L#-U#vQastn)&hL?xe+{g3e4Rfw$n=HrxVx+4uT827@xtN$nX!m{bD)b{f z4|59+$6(1WBKaPYaU_?JOd#>HR^cSMNiW2GY zcKDDqEj@XE{cyfQuaQG9 zNnbP5SKLy^8zxMx5p6phlCn=C>%FHMy{EPbY+%R)Ud5hEz1y-Z4XnJso*8Up2Db=o N(5r+0LbjLP{{q$nZZ`k` literal 1807 zcmb7^KX2Ps5Ww$|;vZ3zR7XwY*sU$uLoFDw7B^E+paxRMP3;=8SD9d>SD6k)s*g`d zlgTN9WcApuP@tcqbBzkHL7+f@!b67wLFMedBc&(=(m@>V-Mzc_AGK->JzP{Zn9KQ z2r3eEsn0~vGlU!x3~9_nFfs%U38pk>BA6M1j)c6-CtqZsZb@qCL-B5VsHMY6%+Q$-AZI=fF+OxKJB)RVof+T#4KjA{AMqwPj<@ufV zIHP14Pd$uQ&^(0}NY(_Y9IZ*Um6O^LNWG~_qsgS%R51TsJog@IC-A+1{{nlIhSzhu zyq=Fu#|hjXcbwRAoL<-&_@vv8^TWXP$DXp|eD8@!((xd49AV?@V-2IZA8Y(aDcra$ zxZ4Q>|0#xYzZ1nb9Ve2m^xBRqrSKjNB#-cD8IO+WW8%AwD$yK@mvCv{I=z2s7g2mtees>RJ&E~n z`KsuCuY7l;ie<~Sv<-#@WLUtoc=ot>Us3au%=hX_oTIA5`N!}2YYIw2+#pdQ@iB_n z_Pt0t9WTO}NRy5YFX=!e<|&FQktVu88cld2?w}Hp^Ci5ltet*)S+4$Gs=e4wV<~K} zej_e*PTZgyZnVRIsw?Pci1nl;k;svei#*9=Q}8Y(!iB(V83f(M!XQC+t;mz0e@DRTPy;%m@e zS?hj$%f50DP zEN72D976Rvr?T45+Qt0(`TY7dpz+26#dx*!)6$vo2b;fE6t;5q#YJWHys|n1G)5{Y LxBsQ>q)Yz+eN$Vb diff --git a/mensa_app/__pycache__/models.cpython-314.pyc b/mensa_app/__pycache__/models.cpython-314.pyc index 7bf82a8ac4b8b196f305182f93ab0d3c76dba9ff..53d1c059876b7332d884071439ec0c4d61dcc3f8 100644 GIT binary patch delta 5843 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 diff --git a/mensa_app/admin.py b/mensa_app/admin.py index 4a2b7fa..8ed593c 100644 --- a/mensa_app/admin.py +++ b/mensa_app/admin.py @@ -3,7 +3,48 @@ from django.contrib import admin # Register your models here. from .models import Person # Ersetze dies durch deine echten Klassennamen -admin.site.register(Person) +class PersonInlineChildren(admin.TabularInline): + """ + Inline für das Feld ``children`` (Schüler eines Elternteils). + Nur bei Eltern (Rolle Mitarbeit/Chef) sichtbar. + """ + model = Person.children.through # Das durchschnittliche Join‑Model + verbose_name = 'Kind' + verbose_name_plural = 'Kinder' + extra = 0 # Keine leeren Zeilen anzeigen + + # ---------------------------------------------------------- + # **WICHTIG:** Hier wird der zu verwendende FK explizit genannt + # ---------------------------------------------------------- + fk_name = 'person' # <-- legt fest, welcher FK gemeint ist + + def has_add_permission(self, request, obj=None): + # Erlaube Hinzufügen nur für Eltern (Rolle Eltern) + return obj and obj.rolle in ('eltern') + + def formfield_for_foreignkey(self, db_field, request, **kwargs): + """ + Verhindere, dass ein Benutzer sich selbst als Kind hinzufügt. + """ + if db_field.name == "person": + # Hier handelt es sich um den FK auf die Person (das Ziel) + kwargs["queryset"] = Person.objects.exclude(pk=self.instance.pk) + return super().formfield_for_foreignkey(db_field, request, **kwargs) + +@admin.register(Person) +class PersonAdmin(admin.ModelAdmin): + list_display = ('user', 'rolle', 'klasse') + search_fields = ['user__username', 'klasse'] + ordering = ('rolle',) + + # ---- Inline für Eltern (Rolle Mitarbeit/Chef) ---- + inlines = [PersonInlineChildren] # Zeigt das Kinder‑Inline nur bei passenden Rollen an + + def has_change_permission(self, request, obj=None): + """ + Optional: Verhindere, dass ein Schüler seine eigene Rolle oder die Zuordnung ändert. + """ + return super().has_change_permission(request, obj) from .models import Schulwoche # Ersetze dies durch deine echten Klassennamen diff --git a/mensa_app/models.py b/mensa_app/models.py index 99d39ad..05735a3 100644 --- a/mensa_app/models.py +++ b/mensa_app/models.py @@ -16,14 +16,59 @@ from PIL import Image as PilImage class Person(models.Model): """Repräsentiert Schüler oder Lehrer.""" user = models.OneToOneField(User, on_delete=models.CASCADE) - rolle = models.CharField(max_length=20, choices=[('schueler', 'Schüler'), ('lehrer', 'Lehrer'), ('mitarbeiter','Mensa-Mitarbeiter'), ('chef','Mensa-Leitung')]) + rolle = models.CharField(max_length=20, choices=[('schueler', 'Schüler'), ('eltern', 'Eltern'), ('lehrer', 'Lehrer'), ('mitarbeiter','Mensa-Mitarbeiter'), ('chef','Mensa-Leitung')]) klasse = models.CharField(max_length=4, blank=True, null=True) # Nur für Schüler relevant + # ---------------------------------------------------------------------- + # Neue Many‑to‑Many Beziehung: „Elternteil ↔ Schüler“ + # ---------------------------------------------------------------------- + children = models.ManyToManyField( + 'self', + blank=True, + related_name='parents', # → für einen Schüler: person.parents.all() + symmetrical=False, # Verhindert eine zirkuläre Rück‑Beziehung (nicht benötigt) + help_text=( + "Eltern können hier die ihnen zugewiesenen Schüler hinzufügen. Für Schüler bleibt dieses Feld leer." + ) + ) + + # ------------------------------------------------------------------ + # Optional: Hilfsmethode, die alle Kinder zurückgibt, ggf. mit einer Sortierung + def get_children_sorted(self): + # Das `order_by('name')` ist ein Beispiel – du kannst deine eigene Sortierung verwenden. + return self.children.order_by('name') + class Meta: verbose_name_plural = "Personen" def __str__(self): - return f"{self.user.username} ({self.rolle})" + if self.children.exists(): + # Wir sammeln alle Kinder‑Namen in einem String, z. B. mit Komma getrennt. + children_names = ', '.join([c.name for c in self.children.all()]) + return f"{self.user.username} ({children_names}) ({self.rolle})" + else: + return f"{self.user.username} ({self.rolle})" + + def clean(self): + """ + Überprüfung für die ManyToMany-Beziehung ``children``: + - Nur Personen mit der Rolle 'mitarbeiter' oder 'chef' (Eltern) dürfen Kinder haben. + - Ein Schüler darf sich selbst nicht als Kind hinzufügen. + """ + super().clean() # ruft die Validierungen der Elternklasse auf (hier User/Model) + + if self.rolle in ('mitarbeiter', 'chef', 'lehrer'): + # Nicht‑Eltern dürfen keine Kinder besitzen + if self.children.exists(): + raise ValidationError( + "Nur Benutzer mit der Rolle 'Eltern' " + "dürfen Schüler zuordnen (children)." + ) + else: + # Eltern: Verhindere, dass sie sich selbst als Kind eintragen + if self in self.children.all(): + raise ValidationError("Ein Benutzer darf nicht gleichzeitig Elternteil und eigener Kind sein.") + class Schulwoche(models.Model):