Ist Django für mein Projekt geeignet?

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

Nö, hilft nicht:

Code: Alles auswählen

[31/Mar/2022 17:54:49] "GET /auswahl/2 HTTP/1.1" 200 1470
Fehler:
<ul class="errorlist"><li>optionen<ul class="errorlist"><li>Bitte eine gültige Auswahl treffen. Dies ist keine gültige Auswahl.</li></ul></li></ul>
[31/Mar/2022 17:54:52] "POST /wahl/2 HTTP/1.1" 200 2
[31/Mar/2022 17:55:11] "GET /auswahl/1 HTTP/1.1" 200 1195
Fehler:
<ul class="errorlist"><li>optionen<ul class="errorlist"><li>Bitte eine gültige Auswahl treffen. Dies ist keine gültige Auswahl.</li></ul></li></ul>
Benutzeravatar
Whitie
User
Beiträge: 216
Registriert: Sonntag 4. Juni 2006, 12:39
Wohnort: Schulzendorf

Das einzige was mir jetzt noch aufgefallen ist: Du verwendest das falsche Feld. Es muss ein ModelMultipleChoiceField sein.

Code: Alles auswählen

optionen = forms.ModelMultipleChoiceField(queryset=Kategorie.objects, widget=forms.CheckboxSelectMultiple, required=False)
Viele Grüße
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Da bin ich wieder! Durch eure positiven Rückmeldungen habe ich einige Dinge wieder selbst hinbekommen!
Aber die nächste Frage:
Ich erstelle eine Übersicht, was der einzelne Nutzer je Kategorie schon erreicht hat. Das sieht z.B. so aus:
Bild
Die Einträge müssen jetzt aber in der richtigen Reihenfolge sortiert werden (0, 1, 2).
im model "Zaehler" ist ein Feld "kategorie" dieses verweist auf das model "Kategorie" und dort befindet sich das Feld "zeile" - danach will ich sortieren. Aber so geht es natürlich nicht:

Code: Alles auswählen

uebersicht = Zaehler.objects.filter(user=user).order_by('Zaehler.kategorie.zeile')
Benutzeravatar
Whitie
User
Beiträge: 216
Registriert: Sonntag 4. Juni 2006, 12:39
Wohnort: Schulzendorf

Warum nimmst du immer Punkte ;-) ?

Code: Alles auswählen

uebersicht = Zaehler.objects.filter(user=user).order_by('kategorie__zeile')
Die Syntax ist wie bei der Abfrage.

Viele Grüße
Whitie

P. S. Funktioniert das mit den Optionen im Formular jetzt eigentlich?
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Whitie hat geschrieben: Samstag 2. April 2022, 10:44 Warum nimmst du immer Punkte ;-) ?
Nun ja, weil ich das ganze anscheinend immer noch nicht verstanden habe (Ich habe sogar 'kategorie_zeile' ausprobiert - aber mit 'zaehler-kategorie_zeile' und Ähnlichem - aber eben mit einem Unterstrich). Also zum Verständnis:
'kategorie_zeile' steht für den Namen einer Variablen, Klasse oder eines Feldes ...?
'kategorie.zeile' bezieht sich auf das Feld innerhalb eines moduls? und
'kategorie__zeile' benutze ich, da das Feld 'zeile' in dem verbundenen modul 'Kategorie ' zu finden ist und nicht in 'Zaehler'?
Whitie hat geschrieben: Samstag 2. April 2022, 10:44 P. S. Funktioniert das mit den Optionen im Formular jetzt eigentlich?
Ja, wunderbar - der Hinweis ist wohl verlorengegangen :wink:

da tauchen aber die nächsten beiden Probleme auf:

Ich will die Rückmeldung der besagten Abfrage jetzt speichern:

Code: Alles auswählen

def optionen(req, slug):
    kategorie = get_object_or_404(Kategorie, slug=slug)
    form = AuswahlForm(kategorie=kategorie)
    user=get_fake_user()    
    zaehler = get_object_or_404(Zaehler, kategorie=kategorie, user=user)
    if req.method == 'POST':
        form = AuswahlForm(req.POST, kategorie=kategorie)
        if form.is_valid():
            zaehler.optionen=form.cleaned_data['optionen']
            #zaehler.save()
            return HttpResponse(zaehler.optionen)
... der Befehl "zaehler.save()" erzeugt die Fehlermeldung: "sub-select returns 5 columns - expected 1"

Und dann noch was Schwieriges:

Ich habe ja schon darauf hingewiesen, dass ich das mit der Objektorientierung wohl auch noch nicht so ganz begriffen habe. Heute nacht hatte ich aber eine Offenbarung und da wäre es schön, wenn ihr mir da auf die Sprünge helfen könntet:

Im view "aufgaben" erzeugt

Code: Alles auswählen

zahl1, zahl2, result =aufgabenstellung(modul.id, user.jahrgang) 
ja die Aufgabe. "aufgabenstellung(modul.id, user.jahrgang) liefert z.B. die Funktion "addieren" zurück:

Code: Alles auswählen

AUFGABEN = {
    1: ergaenzen,
    2: addieren,
    3: subtrahieren,
}

def aufgabenstellung(modul_id, jg):
    return AUFGABEN[modul_id](jg, 3)
    
 def addieren(jg, stufe):
        zahl1=random.randint(5, 45)
        zahl2=random.randint(5, 45)
	...
    	return zahl1, zahl2, zahl1+zahl2  
    	
 def aufgabe(req, slug):   	
 	...
 	zahl1, zahl2, result =aufgabenstellung(modul.id, user.jahrgang)  	     
    
Jetzt muss ich als nächstes dafür sorgen, das die Aufgaben entsprechend den angewählten Optionen und entsprechend dem Jahrgang und der Stufe erstellt werden. Zu diesem Behufe bestehen meine Aufgaben aus verschiedenen Typen. Z.B. müssen die 5.Klässer nur Längen, Massen und Zeiteinheiten umwandeln, später kommen Flächen und Volumeneinheiten dazu. Aufgaben zu Längen sind dann Typ=1, Zeit: Typ=2, Masse: Typ=3, Flächen Typ=4 und Volumen Typ=4.
Die Funktion "def einheiten_umwandeln(5) gibt mir also eine Aufgabe Typ 1-3 zurück und bei jg=9 Aufgaben des Typs 1-5.
Wenn ich das mit der objektorientierung kann ich doch jetzt, wenn ich "def einheiten_umwandeln("")" dafür sorgen, dass ich "typ_anfang=1, typ_ende=3) zurückgeliefert bekomme und bei "def einheiten_umwandeln("mitn Volumeneinheiten") typ_anfang=1 und typ_ende=5 ... oder habe ich da was falsch verstanden. Ich hatte schon angefagen nur für diese Voreinstellungen 35 eigene Funktionen aufzusetzen - das muss ich doch eigentlich nicht? (Habe ich das gut genug erklärt?)

PS. Ich bin ja zur Zeit an der Côte d`Azur und habe nur mein kleines neues Surface dabei, habe es aber geschafft, auch auf diesem meine "Gits zu commiten" (heißt das so?). Macht es für euch Sinn, wenn ich meinen Code in Github freigebe?
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Und schon wieder was:
im model "Zaehler" habe ich die Felder "richtg" und "falsch". Ich wollte auch eine Fehlerquote haben und dann folgendes geschrieben:

Code: Alles auswählen

class Zaehler(models.Model):
...
    richtig = models.PositiveSmallIntegerField(default=0)    
    falsch = models.PositiveSmallIntegerField(default=0)    
...

    def __str__(self):
        return f"({self.user}, {self.kategorie}, {self.aufgnr})"
    
    def quote(self):
        if self.richtig+self.falsch==0:
            quote=0
        else:
            quote=round((self.falsch/(self.richtig+self.falsch))/100,1)
... hätte ja klappen könne - tut es aber nicht.
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Hat sich erledigt. Muss natürlich:

Code: Alles auswählen

    def quote(self):
        if self.richtig+self.falsch==0:
            return 0
        else:
            return round((self.falsch/(self.richtig+self.falsch))/100,1)
... heißen.
Warum kann ich hier eigentlich meinen eigenen Mist nicht löschen?
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Was hätte denn klappen sollen? Und was tut nicht so, wie es soll?
Die Berechnung der Quote ist sehr ungewöhnlich. Gerundet wird erst bei der Ausgabe.

Code: Alles auswählen

    def quote(self):
        gesamt = self.richtig + self.falsch
        return self.falsch / gesamt if gesamt else 0
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Ei, irgenwann lerne ich das auch alles (oder wenigstens einiges).
Danke
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Bei diesem Problem bin ich weitergekommen:
Pitwheazle hat geschrieben: Samstag 2. April 2022, 13:48 Ich will die Rückmeldung der besagten Abfrage jetzt speichern:

Code: Alles auswählen

def optionen(req, slug):
    kategorie = get_object_or_404(Kategorie, slug=slug)
    form = AuswahlForm(kategorie=kategorie)
    user=get_fake_user()    
    zaehler = get_object_or_404(Zaehler, kategorie=kategorie, user=user)
    if req.method == 'POST':
        form = AuswahlForm(req.POST, kategorie=kategorie)
        if form.is_valid():
            zaehler.optionen=form.cleaned_data['optionen']
            #zaehler.save()
           print(zaehler.optionen)
Wenn ich anstelle von zaehler.save() - print(zaehler.optionen) eingebe, kann ich erkennen, dass bei meiner Abfrage nicht eine Liste, sondern ein Dictonary zurückgegeben wird.

"<QuerySet [<Auswahl: mit Kommazahlen>, <Auswahl: noch eine Auswahl>]>"

... und das kann man so ja wohl nicht in ein Textfeld einfügen - ich habe jetzt (natürlich :cry: ) wieder keine Idee, wie ich dies umwandle.
Benutzeravatar
Whitie
User
Beiträge: 216
Registriert: Sonntag 4. Juni 2006, 12:39
Wohnort: Schulzendorf

Du bekommst ein Queryset zurück, das steht da auch. Wie ist denn zaehler.optionen definiert? So wie du es verwenden möchtest, müsste es ein ManyToManyField sein, dann wäre die Verwendung so:

Code: Alles auswählen

zaehler.optionen.set(form.cleaned_data['optionen'])
Viele Grüße
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Ich bräuchte ja eigentlich einfach nur einen String.
Ich dachte so an:

Code: Alles auswählen

            wahlen=form.cleaned_data['optionen']
            for wahl in wahlen:
                optionen=optionen+wahl
... das geht (natürlich ) wieder mal nicht wie ich es gerne hätte. Und zusätzlich haben ja beide Einträge im dict den gleichen Schlüssel.

Jetzt bin ich schon etwas näher:

Code: Alles auswählen

optionen=form.cleaned_data['optionen']
            zaehler.optionen=optionen.values()
            zaehler.save
            return HttpResponse(zaehler.optionen)
ergibt eine Liste und die kann ich scheinbar auch speichern.

Die Ausgabe:
{'id': 2, 'kategorie_id': 2, 'text': 'mit Kommazahlen', 'bis_stufe': 0, 'bis_jg': 7}{'id': 4, 'kategorie_id': 2, 'text': 'noch eine Auswahl', 'bis_stufe': 0, 'bis_jg': 0}
Zur Not könnte ich jetzt mit z.B. "if "noch eine Auswahl" is in zaehler.optionen" die Eingabe auswerten
Benutzeravatar
Whitie
User
Beiträge: 216
Registriert: Sonntag 4. Juni 2006, 12:39
Wohnort: Schulzendorf

Wenn du nur einen String brauchst, probier mal so:

Code: Alles auswählen

wahlen = ';'.join(map(str, form.cleaned_data['optionen']))
Vielleicht reicht das so. Die Stringrepräsentation des Models (Auswahl?) kannst du ja über die __str__ Methode anpassen.

Ich hoffe du möchtest dann nicht irgendwann von diesem String wieder auf das Auswahl Model kommen? Dann wäre es Zeit über ein ManyToManyField nachzudenken.

Viele Grüße
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Prima, das war es!
Bisher wollte ich den String nur auswerten mit z.B. "if "mit" is optionen:"
Benutzeravatar
Whitie
User
Beiträge: 216
Registriert: Sonntag 4. Juni 2006, 12:39
Wohnort: Schulzendorf

Pitwheazle hat geschrieben: Sonntag 3. April 2022, 13:23 ...

ergibt eine Liste und die kann ich scheinbar auch speichern.

Die Ausgabe:
{'id': 2, 'kategorie_id': 2, 'text': 'mit Kommazahlen', 'bis_stufe': 0, 'bis_jg': 7}{'id': 4, 'kategorie_id': 2, 'text': 'noch eine Auswahl', 'bis_stufe': 0, 'bis_jg': 0}
Zur Not könnte ich jetzt mit z.B. "if "noch eine Auswahl" is in zaehler.optionen" die Eingabe auswerten
Hier bekommst du ein QuerySet, das statt Model-Instanzen Dicts enthält. Das sieht mir alles ein wenig verworren aus. Wofür brauchst du die Optionen? Wenn du irgendwas damit auswerten/einstellen möchtest, behalte die Model-Instanzen (Stichwort: ManyToManyField). Wenn du die Optionen unabhängig vom Model speichern willst, nimm ein JSONField und speicher dort eine Liste von Dicts, also:

Code: Alles auswählen

zaehler.optionen = list(form.cleaned_data['optionen'].values())
Beschreibe aber erstmal genauer, was mit zaehler.optionen noch so passiert im weiteren Programm.

Viele Grüße
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Also, am Besten fange ich mal vorne an und versuche mal ein Fluss"diagramm" zu erstellen:

1. Der User wählt eine Kategorie aus (addieren).
2. In dem, von dir erstellte view, wird ein Objekt des moduls "Zaehler" erzeugt (genau eines für diesen User und diese Kategorie "addieren"). Dein View ergänze ich hier um die Abfrage der Optionen, diese sind in dem model "Optionen" gespeichert. Für "addieren" gibt es nur "mit Kommazahlen". Im model "Optionen" steht, dass diese Abfrage bis Jg.7 und Stufe 3 auftaucht. Ist der User im Jg.7 und Stufe 3 kommt also diese Abfrage (die Kommazahlen kommen im Jg.7). Der User kann das schon, oder will es mal probieren und klickt "mit Kommazahlen" an. In "Zaehler.Optionen" wird "mit Kommazahlen" gespeichert.
3. Als nächstes ruft "dein" view "aufgabenstellung(jg=7, Stufe=3, Optionen="mit Kommazahlen" auf und wird dort an die Funktion "addieren" weitergereicht. Diese stellt fest, dass "Optionen" nicht NULL ist und überprüft "if "mit" in Optionen:" gibt als "typ_anfang" 1 zurück und als "typ_ende" die 2 (typ1: ohne Kommazahlen, typ2: mit Kommazahlen). Hat der User die Kommazahlen nicht angeklickt, dann sind beide Werte=1. Das wird zurückgeschickt an den view.
4. Dieser erstellt mit "zahl1, zahl2, ergebnis aufgabenstellung (typ-anfang=1, typ_ende=2)" die entsprechende Aufgabe und speichert diese Werte in "Protokoll"
5. Die Eingabe wird überprüft und ins Protokoll eingetragen, die entsprechenden Zähler in "Zaehler" werden hochgesetzt, die nächste Aufgabe wird erzeugt.
6. Nach drei falschen oder zehn richtigen Aufgaben wird die Übersichtseite aufgerufen und vorher der Aufgabenzähler gelöscht und das Feld "Optionen" in "Zaehler" auf "NULL" gesetzt, daher kommt die Abfrage "mit Kommazahlen" beim nächsten Mal wieder - es sei denn, der User hat mit "mit Kommazahlen" zehn Aufgaben richtig, dann wird in "Schueler" die Stufe von 3 auf 5 hochgesetzt, der User kann das ja jetzt, und die Abfrage kommt nicht mehr, er/sie muss ab Stufe 5 immer (auch in anderen Kategorien" mit Kommazahlen rechnen.

Ich hoffe, das bekomme ich alles hin.

Wo ich noch nicht so recht weiß wie das geht: Der User kann die Abfolge von 10 Aufgaben abbrechen (z.B. weil sie ihm zu schwer sind). Dazu klickt er in der Titellzeile auf "Startseite" und kommt auf ebendiese. Auch in diesem Fall müsste der Aufgabenzähler in "Zaehler" zurückgesetzt werden und ebendort "optionen" auf "Null" (damit die Abfrage u.U. wiederkommt).

Ich hoffe, das ist so nachvollziebar und erscheint als machbar.

Grüße und Dank
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Ich habe nochmal nachgedacht und wollte es jetzt doch mal deinen Vorschlag mit ManyToManyField ausprobieren. Ich habe also in "Zaehler" ein ManyToManyFeld eingefügt:

Code: Alles auswählen

class Zaehler(models.Model):
    user = models.ForeignKey(Schueler, verbose_name='Benutzer', related_name='zaehler', on_delete=models.CASCADE)    
    kategorie = models.ForeignKey(Kategorie, on_delete=models.CASCADE, related_name="zaehler")
    
    optionen = models.ManyToManyField(Auswahl)
    #optionen=models.CharField(max_length=100, blank=True, verbose_name="Optionen")
Im Adminbereich sehe ich jetzt, dass alle möglichen Optionen von allen Kategorien zur Wahl stehen:
Bild
... das stimmt so wohl eher nicht. Da sollten doch wahrscheinlich nur die stehen, die zur Kategorie "addieren" passen.

Im View habe ich folgendes geändert:

Code: Alles auswählen

def optionen(req, slug):
    kategorie = get_object_or_404(Kategorie, slug=slug)
    form = AuswahlForm(kategorie=kategorie)
    user=get_fake_user()    
    zaehler = get_object_or_404(Zaehler, kategorie=kategorie, user=user)
    if req.method == 'POST':
        form = AuswahlForm(req.POST, kategorie=kategorie)
        if form.is_valid():
            zaehler.optionen.set(form.cleaned_data['optionen'])
            #zaehler.optionen = ';'.join(map(str, form.cleaned_data['optionen']))
            #zaehler.save
            return HttpResponse(zaehler.optionen)
Da kommt zumindest keine Fehlermeldung. Wenn ich aber zwei Optionen auswähle kommt die HTTP Meldung "core.Auswahl.None"
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Ach und dann nochwas (Ich stelle hier wirklich wieder fest, dass mir schwer fällt komplexere Zusammenhänge zu überblicken): vielleicht kann mir da jemand auf die Sprünge helfen:
Im model "Protokoll" wird für jede Aufgabe eine neue Instanz (das heißt doch so?) erzeugt in der ich alles mögliche speichere. Unter anderem steht da der "Typ" drin. Dieser entspricht genau einer Frage im model "Frage". Es wäre schön, wenn in der Protokollinstanz eine Relation zur entsprechenden Frage stünde, aber auch das bekomme ich nicht hin. Das steht im model "Protokoll":

Code: Alles auswählen

class Protokoll(models.Model):
    user = models.ForeignKey(Schueler, verbose_name='Benutzer', related_name='protokoll', on_delete=models.CASCADE)

    kategorie = models.ForeignKey(Kategorie, verbose_name='Kategorie', related_name='protokoll', on_delete=models.CASCADE)
    #frage = models.ForeignKey(Frage, verbose_name='Frage', related_name='protokoll', on_delete=models.CASCADE)
    typ = models.CharField(max_length=5, blank=True )

    #der Aufgabentext:
    text = models.TextField(blank=True)

    #hier speichere ich die Lösung, wahlweise als zahl, u.U. auch (mehrere) Lösungen als String:
    value = models.DecimalField('Wert', max_digits=20, decimal_places=7)
    loesung = models.CharField(max_length=20, blank=True, verbose_name="Lösung")
    ...
... irgendwie muss ich das doch mit "frage = models.ForeignKey(Frage, verbose_name..." einbinden, aber wie?
Benutzeravatar
Whitie
User
Beiträge: 216
Registriert: Sonntag 4. Juni 2006, 12:39
Wohnort: Schulzendorf

Pitwheazle hat geschrieben: Montag 4. April 2022, 16:42
Im Adminbereich sehe ich jetzt, dass alle möglichen Optionen von allen Kategorien zur Wahl stehen:

... das stimmt so wohl eher nicht. Da sollten doch wahrscheinlich nur die stehen, die zur Kategorie "addieren" passen.
Natürlich siehst du alle Optionen, deinen Zusammenhang kennt der Admin-Bereich nicht. Eigentlich gibt es für solche Fälle das Argument .limit_choices_to. Das kannst du hier aber nicht verwenden, da beim erstellen des Models "self" noch nicht bekannt ist und du an self.kategorie kommen müsstest. Für den Admin-Bereich kannst du das über einen angepassten ModelAdmin machen. Es wird aber nie für ein neues Zaehler-Objekt funktionieren, da die Kategorie dort noch nicht feststeht. Da würde dann nur noch Javascript helfen.
Aber in deinen Views kannst du alles so anpassen wie du es brauchst, da kommst du an alle Informationen.

Was funktioniert denn an deinem Protokoll nicht? Fehlermeldung? Der ForeignKey sieht erstmal OK aus. So nebenbei: Der related_name sollte "protokolle" heißen, da man in diese Richtung viele Objekte erhält.

Viele Grüße
Whitie
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Hallo und Danke wiedermal.
Ich bin wieder etwas am Verzweifeln und verliere immer wieder den Überblick. Die Dinge, die ich zum Laufen gebracht hae sind immer mal wieder weg. Ich befürchte, dass das daran liegt, dass ich auch mit git nicht umgehen kann. Wenn ich glücklich abends alles zum Laufen bekommen habe, dann klicke ich bei Git auf Änderungen übernehmen und schicke das dann an Github. Am nächsten Tag stelle ich dann fset, dass ich wieder eine Version von vor einigen Tagen vor mir habe. Wahrscheinlich klicke ich da wieder Sachen an, von denen ich nichts verstehe.

Also das mit der Auswahl der Optionen funktioniert jetzt nicht mehr, da kümmere ich mich morgen noch mal drum und lasse heute die Finger von Git.

Zu deiner Frage bezüglich des Protokolls: Ich hatte ja geschrieben, dass ich aus dem Protokoll gerne auf die Frage zugreifen möchte und daher

Code: Alles auswählen

frage = models.ForeignKey(Frage, verbose_name='Frage', related_name='protokoll', on_delete=models.CASCADE)
ergänzt. Allerdings stimmt es nicht, dass die Frage mit dem Typ übereinstimmt, das muss ich auch nochmals überdenken. Jetzt habe ich aber das Problem, dass ich mit "makemigrations" dieses Feld hinzugefügt habe, bei "migrate" aber einen Fehler erhalte;

Code: Alles auswählen

The row in table 'core_protokoll' with primary key '1' has an invalid foreign key: core_protokoll.frage_id contains a value '0' that does not have a corresponding value in core_frage.id.
Wenn ich das richtig verstehe verträgt sich dieser Eintrag nicht mit den Objekten, die in meinen Protokollzeilen schon drinstehen. Ich habe daher alle Einträge in den Protokollen gelöscht, das hat aber nichts geholfen. Daher habe ich jetzt das Feld "frage" im model "protokoll halt wieder gelöscht.
Antworten