Ist Django für mein Projekt geeignet?

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

Hallo Leute ... es ist noch ein langer Weg zum Verständnis ... ich brauche wieder Hilfe:

Also wenn ein user eine neue Kategorie anwählt, wird ein neues Objekt (ist das ein Objekt oder eine Instanz? ... die Benennungen kann ich auch immer noch nicht!) des models "Zaehler" erstellt. Bisher habe ich nur mit Kategorien gearbeitet zu denen ein derartiger "Eintrag" besteht. Bisher funktionierte das:

Code: Alles auswählen

        zaehler = get_object_or_404(Zaehler, kategorie = kategorie, user = user)

Wenn ich jetzt eine neue Kategorie anwähle, bekomme ich natürlich eine Fehlermeldung . Ich habe den Code also geändert in:

Code: Alles auswählen

        zaehler = Zaehler.objects.get_or_create(user = user, kategorie = kategorie)
und bekomme einen Fehler - aber erst, wenn ich auf ein Feld zugreifen will

Code: Alles auswählen

if zaehler.optionen_text == "": 

Code: Alles auswählen

 	
'tuple' object has no attribute 'optionen_text'
... und warum ich "get_object_or_404" schreibe weiss ich auch nicht genau. Mir ist klar, dass, wenn das Objekt nicht existiert, auf die entsprechende Fehlerseite umgeleitet werde, aber was schreibe ich, wenn ich sicher sein kann, das es existiert?
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Eine grundlegende Fähigkeit beim Programmieren ist es, die Dokumentation lesen zu können: https://docs.djangoproject.com/en/4.0/r ... -or-create
Returns a tuple of (object, created), where object is the retrieved or created object and created is a boolean specifying whether a new object was created.
Objekt und Instanz kann man meist synonym benutzen. Instanz wird benutzt, wenn man betonen möchte, dass man eine Instanz einer spezifischen Klasse erzeugt.
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Lieber Sirius, bitte glaube mir, ich habe das gelesen (sonst hätte ich ja den Code auch nicht nutzen können) wohlan, man muss das auch noch verstehen. Das ist nicht nur ein Problem mit der englischen Sprache (im normalen Leben bin ich dieser, glaube ich durchaus mächtig). Ich hatte zumindest den ersten Teil "Eine bequeme Methode zum Suchen eines Objekts mit den angegebenen Kwargs (kann leer sein, wenn Ihr Modell Standardwerte für alle Felder hat), und zum Erstellen eines Objekts, falls erforderlich" so interpretiert, dass der Code das macht was ich will. In dem angegebenen Beispiel, in dem beschrieben wird, was passiert:

Code: Alles auswählen

try:
    obj = Person.objects.get(first_name='John', last_name='Lennon')
except Person.DoesNotExist:
    obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
    obj.save()

, sieht es doch auch so, als wäre es das, was ich will.
Ich lese die Dokumentation und hoffe, sie früher oder später auch besser zu verstehen, aber leider hilft mir das in meinem derzeitigen Verständnishorizont leider nicht weiter. :? War das bei Dir anfänglich anders?
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Pitwheazle hat geschrieben: Donnerstag 9. Juni 2022, 17:54 ... und warum ich "get_object_or_404" schreibe weiss ich auch nicht genau. Mir ist klar, dass, wenn das Objekt nicht existiert, auf die entsprechende Fehlerseite umgeleitet werde, aber was schreibe ich, wenn ich sicher sein kann, das es existiert?
Aber zumindest das funktioniert jetzt mit

Code: Alles auswählen

zaehler = Zaehler.objects.get(kategorie = kategorie, user = user)
wobei ich nicht so recht weiß, wann ich das "...or_404.." benutzen sollte.
Zuletzt geändert von Pitwheazle am Donnerstag 9. Juni 2022, 23:16, insgesamt 1-mal geändert.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Pitwheazle: Das Beispiel ist doch aber nicht für die `get_or_create()`-Methode sondern für `get()` zur Illustration, dass da eine Wettlaufsituation dabei auftreten kann. Das Beispiel für `get_or_create()` kommt danach.

Und spätestens nach der Ausnahme das Tupel kein Attribut `optionen_text` haben, hätte man ja gewusst, das `zaehler` ein Tupel ist und hätte sich a) dessen Wert mal anschauen können, und dann noch mal in die Dokumentation schauen können was die Methode zurück gibt. Es steht da ja in Worten und in einem Code-Beispiel.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Vielen Dank, jetzt klappt es! Bitte bleibt weiter so geduldig! Stückchen für Stückchen reift mein Wissen.
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

ich hatte hier die Frage gestellt, wie ich es bewerkstelligen kann, je 7 Zeilen einer Tabelle in einer eigenen Farbe einzufärben. Zwischenzeitlich habe ich mit:

Code: Alles auswählen

    tr:nth-child(1n+1) {
        background-color: #FFCCCC;
       }

    tr:nth-child(1n+8) {
     background-color: #FFFF00;
    }...
wohl eine Möglichkeit gefunden. Das muss ich jetzt ausprobieren.
(Mir ist es nicht gelungen, diesen Beitrag zu löschen)
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Ich brauche euch doch wieder. Der oben angegebene Code wirkt auch auf die Überschrift:Bild ... wie bekomme ich das weg?
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Überschrift sollte in einem <thead>-Element sein, dessen Styles sind dann unabhängig von denen im <tbody>.
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Und wie mache ich das? Noch steht das alles untereinander. Hier ist der Ausschnitt aus dem CSS Code:

Code: Alles auswählen

thead {
  background-color:lightgrey;
}

tbody {
  background-color:whitesmoke;
}

tr {
  text-align: center;
}

tr:nth-child(1n+1) {
    background-color: #B0E2FF;
    }

tr:nth-child(1n+8) {
    background-color: #9AFF9A;
}

th {
  padding:0.5rem 1rem;
  height: 3rem;
}

td {
  padding:0.5rem 1rem;
  height: 1rem;
}
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Und nochwas:
Zurzeit habe ich Aufgaben für die ersten fünf Kategorien. Jede Kategorie entspricht einer Funktion. Die Aufgaben erstelle ich aus

Code: Alles auswählen

typ, text, pro_text, lsg, result = aufgaben(kategorie.id, jg = user.jg, stufe = user.stufe, typ_anf = zaehler.typ_anf, typ_end = zaehler.typ_end, optionen = "") 
Ich übergebe (zurzeit noch) 6 Parameter und bekomme (zurzeit) 5 Rückgabewerte. Das mit optionalen Parametern habe ich hinbekommen. Gibt es aber auch optionale Rückgabewerte? Für einige Kategorien werde ich mehr Rückgabewerte brauchen, vor allem, wenn ich noch Grafiken erzeugen muss. Muss ich dann in allen Funktionen für jeden der Werte einen, unter Umständen, leeren Wert zurückgeben oder kann ich auch die 5 bestehenden zurückgeben auch wenn in dem obenstehenden Code mehr erwartet werden? Also z.B.

Code: Alles auswählen

typ, text, pro_text, lsg, result, nocheinwert, undnocheinwert = aufgaben(kategorie.id, jg ....) 
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Naja, Du mußt doch nur den Selector entsprechend einschränken:

Code: Alles auswählen

tbody > tr:nth-child(1n+1)
Und zur nächsten Frage:
Statt fünf einfache Werte zurückzuliefern, gibtst Du immer einen komplexen Wert zurück.
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Sirius3 hat geschrieben: Samstag 18. Juni 2022, 17:12

Code: Alles auswählen

tbody > tr:nth-child(1n+1)
Danke! Gewusst wie! In meinem Buch "CSS für Anfänger" steht sowas nicht drin (oder ich habe es überlesen?)
Sirius3 hat geschrieben: Samstag 18. Juni 2022, 17:12 Und zur nächsten Frage:
Statt fünf einfache Werte zurückzuliefern, gibtst Du immer einen komplexen Wert zurück.
Also in Mathe hebe ich gelernt, dass die komplexen Zahlen eine Erweiterung der Reellen Zahlen sind. Ich habe jetzt halt einfach in allen Funktionen "" ergänzt:

Code: Alles auswählen

return typ, text, pro_text, lsg, "", result
Ist es das was du meinst? .. ich hatte gehofft, dass ich das umgehen kann - vor allem, wenn da noch mehr dazukommen.
Benutzeravatar
Whitie
User
Beiträge: 216
Registriert: Sonntag 4. Juni 2006, 12:39
Wohnort: Schulzendorf

Du könntest ein Wörterbuch (dict) mit den Werten zurückgeben.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Komplexer Rückgabewert hat nichts mit komplexen Zahlen zu tun. Mit komplex meinte ich eine Objekt, das aus mehreren Werten besteht. Und da die Aufgabe je nach Aufgabentyp wahrscheinlich auch anders dargestellt werden soll, landen wir bei einem Objekt, zu dem auch noch Methoden gehören. Das heißt, am besten definierst du dir für jeden Aufgabentyp eine eigene Klasse.
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

So, heute geht´s mal wieder weiter (Ich habe ja gedacht, das könne nicht sein, aber Rentner haben wirklich keine Zeit :wink: )
Whitie hat geschrieben: Sonntag 19. Juni 2022, 05:38 Du könntest ein Wörterbuch (dict) mit den Werten zurückgeben.
Das mit dem dict habe ich auch schon bedacht. Aber hilft mir das weiter? Mein Funktionsaufruf

Code: Alles auswählen

 typ, text, pro_text, lsg, hilfe, result = aufgaben(kategorie.id, j....) 
erwartet doch trotzdem (zurzeit noch) 6 Rückgabewerte und die Funktionen müssen die doch auch zurückgeben. Das ändert sich doch nicht wenn ich ein dict zurückgebe - oder doch?
Sirius3 hat geschrieben: Sonntag 19. Juni 2022, 06:44 ... Das heißt, am besten definierst du dir für jeden Aufgabentyp eine eigene Klasse.
... auweh, um die Klassen drücke ich mich ja schon lange rum. Aber ich denke, früher oder später muss ich da auch dran. Lass mir noch etwas Zeit bis ich zu den Grafiken komme.

Jetzt habe ich wieder was neues, mit dem mein armes Hirn überfordert ist:
Klicken auf "Lösung anzeigen" zeigt die Lösung der aktuellen Aufgabe und zeigt gleichzeitig eine neue Aufgabe an:
Bild
Klicken auf "Hilfe anzeigen" zeigt, wie gewünscht, eine Hilfe an und ebenfalls eine neue Aufgabe. Ich will aber keine neue Aufgabe.
Zum Vergleich: Wenn der User die Aufgabe richtig gelöst hat, bekommt er eine neue Aufgabe, falls die Eingabe nicht richtig war, bekommt er noch zwei weitere Chancen. Das alles läuft in der Funktion "main" zusammen. (Die Grundstruktur ist immer noch die von Whitie.) Ich kürze die hier mal etwas:

Code: Alles auswählen

def main(req, slug, protokoll_id=0):                                                        #hier läuft alles zusammen
    kategorie = get_object_or_404(Kategorie, slug = slug)
    kategorie_id = kategorie.id
    user = get_fake_user()    
    if req.method == 'POST':  
        protokoll = Protokoll.objects.get(pk = req.session.get('eingabe_id'))
        ...
        zaehler = Zaehler.objects.get(pk = req.session.get('zaehler_id'))
	...
        if form.is_valid():                                                 #Aufgabe beantwortet
            eingabe = form.cleaned_data['eingabe']
	...
            if kontrolle(eingabe, right):                                   #Anwort richtig
               ....
                return redirect('main', slug)
            else:                                                           #Antwort falsch
	...
    else:                                                                   #Aufgabenstellung
        zaehler, created = Zaehler.objects.get_or_create(user = user, kategorie = kategorie)
        form = AufgabeFormZahl()
        user = get_fake_user()
	...
        typ, text, pro_text, lsg, hilfe, result = aufgaben(kategorie.id, jg = user.jg, stufe = user.stufe, typ_anf = zaehler.typ_anf, typ_end = zaehler.typ_end, optionen = "") 
        protokoll = Protokoll.objects.create(
            user = user, kategorie = kategorie, text = pro_text, value = result, loesung = lsg, hilfe = hilfe        
        )                                                                   #Protokoll wird erstellt
        req.session['eingabe_id'] = protokoll.id    
        req.session['zaehler_id'] = zaehler.id   
	---
    context = dict(kategorie = kategorie, typ = typ, aufgnr = zaehler.aufgnr, text = text, form = form, zaehler_id = zaehler.id, hilfe = protokoll.hilfe,)
    return render(req, 'core/aufgabe.html', context)
Wenn die Aufgabe falsch beantwortet wurde, geht es im Code mit

Code: Alles auswählen

    context = dict(kategorie = kategorie, typ = typ, aufgnr = zaehler.aufgnr, text = text, form = form, zaehler_id = zaehler.id, hilfe = protokoll.hilfe,)
    return render(req, 'core/aufgabe.html', context)
weiter. Klicken auf "Hilfe anzeigen" führt zur Funktion

Code: Alles auswählen

def hilfe(req, zaehler_id):
    zaehler = get_object_or_404(Zaehler, pk = zaehler_id)
    zaehler.hilfe +=1
    protokoll = Protokoll.objects.filter(user = zaehler.user).order_by('-id').first()
    msg=f'{protokoll.hilfe}'    
...
    zaehler.hinweis = msg
    zaehler.save()   
    return redirect('main', zaehler.kategorie)
Ich dachte mir jetzt, wenn ich mittels

Code: Alles auswählen

return redirect('main', zaehler.kategorie, protokoll.id)
den entsprechenden Eintrag in "Protokoll" übergebe (da stehen die gewünschten Werte drin) und dann

Code: Alles auswählen

protokoll = Protokoll.objects.create(
durch

Code: Alles auswählen

protokoll = Protokoll.objects.get_or_create(
ersetze, sollte das doch klappen. Ich bekomme es aber nicht hin.
Mir fällt dann schon nicht ein, wie ich den Aufruf von "main"

Code: Alles auswählen

    path('<slug:slug>/', views.main, name='main'),
entsprechend anpasse.
...
Könnt ihr mir mittels dieser Angaben und Codeteile weiterhelfen?
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

So, ich habe jetzt einfach ein/en neue/n/s (welches Geschlecht? oder divers?) view erstellt:

Code: Alles auswählen

def hilfe(req, zaehler_id, protokoll_id):
    zaehler = get_object_or_404(Zaehler, pk = zaehler_id)
    protokoll = get_object_or_404(Protokoll, pk = protokoll_id)
    msg=f'{protokoll.hilfe}'    
    messages.info(req, f'{zaehler.hinweis}')  
    protokoll.eingabe = "Hilfe"
    protokoll.save()
    zaehler.hilfe +=1
    zaehler.hinweis = msg
    zaehler.save()
    form = AufgabeFormZahl()
    context = dict(kategorie = protokoll.kategorie, typ = protokoll.typ, aufgnr = zaehler.aufgnr, text = protokoll.text, form = form, zaehler_id = zaehler.id, hilfe = protokoll.hilfe, protokoll_id = protokoll.id)
    return render(req, 'core/aufgabe.html', context)
... sicher nicht sehr elegant, es funktioniert aber (noch).

Nachtrag:
Zu früh gefreut. Ich muss zweimal auf "Hilfe anzeigen" klicken, bis die Hilfe auch angezeigt wird.
Ich vermue, das hängt mit der Zeile

Code: Alles auswählen

    messages.info(req, f'{zaehler.hinweis}')  
zusammen
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Die ersten 7 Aufgabenkategorien sind fertig. Jetzt wird es aber schwieriger. Jetzt kommen nicht nur Rechenaufgaben sondern auch Grafiken und Ergebnisse, die ich als Text auswerten muss. Mein erstes Problem ist jetzt die Umwandlung von Zahlen in Zahlwörter also "schreibe vierundfünfzigtausendfünfzehn in Ziffern". Dafür gibt es ja schon Leute, die haben die entsprechenden Bibliotheken schon erstellt:
https://github.com/savoirfairelinux/num2words
... den Code habe ich runtergeladen. Stehe jetzt aber (mal wieder) auf dem Schlauch: Wie integriere ich das und wie greife ich darauf zu?
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Indem Du die Dokumentation dazu liest!?
In code there's only one function to use:

Code: Alles auswählen

>>> from num2words import num2words
>>> num2words(42)
forty-two
>>> num2words(42, to='ordinal')
forty-second
>>> num2words(42, lang='fr')
quarante-deux
Pitwheazle
User
Beiträge: 869
Registriert: Sonntag 19. September 2021, 09:40

Doch, habe ich gelesen. Aber ich habe noch grundlegendere Probleme (und muss feststellen, dass mir grundlegende Erkenntnisse fehlen): Wo packe ich den ganzen Code hin? Kommt der in meine Views oder muss ich dafür einen neuen Ordner anlegen? Oder kann ich den irgenwo anders hinpacken? Muss ich den ganzen Code verwenden? Ich habe nur die Klasse "lang_DE.py" in meine Views kopiert. Dann bekam ich zumindest schon eine Fehlermeldung für die Zeile "from .lang_EU import Num2Word_EU". Aber wenn ich den Code in meine Views packe, macht die Zeile "from num2words import num2words" keinen Sinn... Und es ist mir auch nicht gelungen, von der Schell darauf zuzugreifen. Es steht auch in der Dokumentation
The easiest way to install num2words is to use pip:
pip install num2words
(du siehst: ich habe sie schon gelesen)
Habe ich auch gemacht, aber was dann ...? Es ist mir ja schon ziemlich peinlich - aber du siehst, mir fehlen grundlegende Fertigkeiten und ich habe einfach keine Ahnung von garnichts!
Antworten