filter und related für Dummies

Django, Flask, Bottle, WSGI, CGI…
Antworten
Pitwheazle
User
Beiträge: 873
Registriert: Sonntag 19. September 2021, 09:40

Also ganz habe ich das immer noch nicht kapiert und muss immer wieder rumprobieren - manchmal klappt das - meistens erstmal nicht.
Also ein Beispiel:
In meinen "Protokoll"en steht ein "user"

Code: Alles auswählen

class Protokoll(models.Model):
    user = models.ForeignKey(Profil, verbose_name='Benutzer', related_name='protokolle', on_delete=models.CASCADE)
    ..
... da habe ich " related_name='protokolle'" eingetragen - weiß aber ehrlich gesagt nicht warum.
Dieser user ist ein OneToOne Relation zu "Profil":

Code: Alles auswählen

class Profil(models.Model):
    user = models.OneToOneField(User, related_name='profil', on_delete=models.CASCADE )
    nachname = models.CharField(max_length=30)
    vorname = models.CharField(max_length=30)    
    klasse = models.CharField(max_length=10)
    schule = models.ForeignKey(Schule, related_name='schule1', null= True, blank=True, on_delete = models.SET_NULL)
    lerngruppe = models.ForeignKey(Lerngruppe, null= True, blank=True, on_delete = models.SET_NULL, related_name='gruppe')
    ...
Mir ist es gelungen ein Queyset (heißt das so?) zu erzeugen und erstmal alle Einträge von Usern ohne Lerngruppe herauszufiltern:

Code: Alles auswählen

protokoll = Protokoll.objects.filter(user__lerngruppe = None)  
mir ist es sogar gelungen, diejenigen auszuschließen, die ich in die Admingruppe "Lehrer" aufgenommen habe:

Code: Alles auswählen

       protokoll = protokoll.filter(~Q(user__user__groups__name = 'Lehrer'))
Die Lehrer/innen sollen bei der Anmeldung als Klassenbezeichnung zunächst "Lehrer" eintragen - diejenigen möchte ich auch rausfiltern (ob das jetzt sinnvoll ist oder nicht)

Code: Alles auswählen

 protokoll = protokoll.filter(~Q(user__klasse = "lehrer"))
- das gelingt aber nicht - die werden immer noch angezeigt.
Könnt ihr mir an diesem Beispiel bitte zunächst erklären, was da falsch ist, aber auch wofür (oder wie) ich dieses "related_name" benutze ... und dann habe ich das immer noch nicht verstanden, wo ich diesen doppelten Unterstrich benutze - manchmal scheint ja auch nur einer benötigt zu werden (oder sind das immer Benennungen?).
Bitte für Dummies - an der Dokumentation habe ich mich schon versucht.
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Der "related name" ist das, was der Name aussagt. Das geht besser auf, wenn man die anderen Bezeichnungen nicht auf Deutsch oder Denglisch wäht.
Es ist der Name der Relation, von der anderen Seite aus gesehen.

Nicht lauffähiger Code zur Verdeutlichung:

Code: Alles auswählen

class A(Model):
....

class B(Model):
    a = ForeignKey(A, related_name="bs")
Wenn eine Instanz von "B" angelegt wird, kann man die Instanz von "A", auf die verwiesen wird mit ".a" erreichen.
Da es sich um eine 1:n Beziehung (1 "A" : n "B") muss man ja auch irgendwie von der der einen A-Instanz zu den Bs kommen. und das macht man über den related name. Von einer Instanz von a kann kommt man über .bs.all() an alle related "Bs".

Und "lehrer" ist nun mal etwas anderes als "Lehrer". Und wenn man die Groß/Kleinschreibung nicht berücksichtigen möchte, dann muss man dafür auch die reichtige Funktion suchen.

Und warum benutzt du da jetzt ein "Q" und nicht einfach ".exclude" statt ".filter"?
Pitwheazle
User
Beiträge: 873
Registriert: Sonntag 19. September 2021, 09:40

Vielen Dank dafür - jetzt muss ich noch damit umgehen lernen. Das hier ist also Qutsch:

Code: Alles auswählen

class Protokoll(models.Model):
    user = models.ForeignKey(Profil, verbose_name='Benutzer', related_name='protokolle', on_delete=models.CASCADE)
... da sollte ich 'protokolle' z.B. durch 'benutzer' ersetzen.
Zwei neue Fragen dazu
  • kann man diese Namen in verschiedenen Tabellen auch doppelt vergeben?
    Was bewirkt in diesem Zusammenhang der doppelte Unterstrich?
Apropo 'benutzer':
Bei den deutschen Begriffen erkenne ich sofort, dass sie von mir sind (Jetzt wo ich darüber nachdenke, sollte ich auch 'user' durch 'nutzer' ersetzen). Die meisten Programmierer nutzen englische Begriffe ... aber in der EU sprechen 21% Deutsch als Muttersprache, 14% Französisch und nur 13% Englisch (ebenso wie Italienisch). (Das Deutsch fast Amtssprache auch in den USA geworden wäre (wie mein Vater behauptete) ist ein Gerücht - aber auch weil die USA wohl keine offizielle Amtssprache hat). Aber ich wundere mich immer wieder, wenn ich Gebrauchsanweisungen in einem dicken Handbuch aus der Verpackung ziehe und französisch oder auch norwegisch vor der deutschen Anleitung kommt - englisch sowieso.
Auch der erste Computer wurde in Deutschland gebaut (auch wenn das Amerikaner nicht immer wahrhaben wollen).
Lass' mir doch den Spleen.
sparrow hat geschrieben: Sonntag 21. Januar 2024, 22:56 Und warum benutzt du da jetzt ein "Q" und nicht einfach ".exclude" statt ".filter"?
... nun ja, das mit '.exclude' habe ich zwar schonmal gelesen, aber noch nie angewandt, das hatte ich nicht auf der Schirm - geht prima! Ich hatte nach dem Problem gesucht, dass ich '!=' nicht im Filter anwenden kann und dann die Lösung mit 'Q' gefunden.
Und zum eigentlichen Problem:
Ich habe eine Benutzergruppe 'Lehrer' angelegt. Die hat nichts mit meinem Problem zu tun. Die Benutzer müssen bei der Anmeldung außer ihrem Namen auch ihr Schuljahr und die besuchte Schulform bzw. Leistungsstufe eingeben, da diese u.A. über den Schwierigkeitsgrad der Aufgaben entscheiden. Das müssen Lehrkräfte auch, diese können diese Zuordnung aber, im Gegensatz zu den Lernenden, jederzeit ändern. Für die bessere Identifikation müssen die Lernenden auch ihre Klassenbezeichnung eingeben - die haben Lehrkräfte nicht, die bekommen den Hinweis, dass sie hier 'Lehrer' eingeben sollen. Einige werden auch 'lehrer' eingeben oder 'LEHRER' - da kümmere ich mich noch drum. Aber egal was ich hier im Code eingebe, es wird niemand ausgefiltert. Auch wenn ich hier '8.4' eingebe, sehe ich die entsprechenden Nutzer noch.
So sieht das jetzt aus:

Code: Alles auswählen

        protokoll = Protokoll.objects.filter(user__gruppe = None)                 # alle Protokollobjekte der Schülerinnen und Schüler ohne Gruppenzugehörigkeit
        protokoll = protokoll.exclude(user__user__groups__name = 'Lehrer')
        protokoll = protokoll.exclude(user__klasse = "Lehrer")
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

Pitwheazle hat geschrieben: Montag 22. Januar 2024, 14:18 Vielen Dank dafür - jetzt muss ich noch damit umgehen lernen. Das hier ist also Qutsch:

Code: Alles auswählen

class Protokoll(models.Model):
    user = models.ForeignKey(Profil, verbose_name='Benutzer', related_name='protokolle', on_delete=models.CASCADE)
... da sollte ich 'protokolle' z.B. durch 'benutzer' ersetzen.
Zwei neue Fragen dazu
  • kann man diese Namen in verschiedenen Tabellen auch doppelt vergeben?
    Was bewirkt in diesem Zusammenhang der doppelte Unterstrich?
Warum willst du denn da "protokolle" durch "benutzer" ersetzen?
Das ist der Name um von dem verbundenen User Objekt auf dessen Protokolle zuzugreifen.
Was genau du mit doppelten Unterstrichen in diesem Zusammenhang meinst, verstehe ich leider nicht. Was doppelte Unterstriche in Queries machen, hat Black Jack schon einmal in einem deiner Threads ausführlich erklärt. Das müsste man noch finden.

Die Query sieht erst einmal nicht falsch aus, auch wenn ich die verschiedenen Parameter in einem exclude zusammenfassen würde - nicht auf 3 aufteilen.
Ansonsten würde ich mal exact oder iexact versuchen oder umgedreht mal versuchen ob man das _selektieren_ statt ausschließen kann. Denn vielleicht ist da gar nicht der Wert enthalten, den du vermutest.
Antworten