Python - Sqlite3 = Abgleich Datenbank und Eingabe

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
Linux-Fan
User
Beiträge: 8
Registriert: Samstag 30. Mai 2020, 15:03

Hallo,

ich versuche mich seit kurzem, mit Python in Verbindung mit einer Sqlite-Datenbank vertraut zu machen.

Als erstes Projekt habe ich mich für einen ganz einfachen Vokabeltrainer entschieden.
Ich bin mit meinen Fortschritten sehr zufrieden und habe auch immer wieder im Internet Lösungen für meine Probleme gefunden.
Aktuell komme ich aber mit googeln nicht weiter.

Die Sqlite-Datenbank ist erstellt und mit einigen Datensätzen gefüllte.

Die Aufnahme neuer Vokabeln in die Datenbank funktioniert schon. Jetzt versuche ich gerade die ersten Schritte im Zusammenhang mit der Vokabelabfrage.

Bevor ich im in späteren Schritten noch Schleifen zur mehrfachen Abfrage von Vokabeln einbauen möchte, wollte ich zuerst einmal die Abfrage einer vorgegebenen Vokabel testen. Hier z.B. die Vokabel mit der (laufenden) Nr. 430. Zusammen mit der späteren Schleife sollen die Vokabeln dann per Variable immer um einen Datensatz von hinten nach vorne abgefragt werden. Zum Beispiel zuerst Nr. 430 danach Nr. 429 etc. Wie dieses mit einer Schleife funktioniert, müsste ich hoffentlich hinbekommen. Die Einbindung der Variablen "aktuellenr" im Befehl "select" hinter "where" bekomme ich aber noch nicht hin. Vielleicht kann mir da jemand auch schon mal einen Tipp geben.

Zum Testen lasse ich mir die deutsche und die englische Vokabel anzeigen. Später wird die Anzeige der englischen Vokabel natürlich noch entfernt, da es ja sonst keinen Sinn macht. Es zeigt mir jedoch, dass ich die Vokabeln aus der Datenbank richtig geholt habe.

Ich stehe jetzt aber noch bei dem Problem, dass der Abgleich der englische Vokabel aus der Datenbank mit der Tastatureingabe nicht funktioniert. Mir wird immer das Ergebnis "Falsche Antwort" gegeben, auch wenn ich die Vokabel richtig eingebe. Ich vermute, dass die Variablen aus der Datenbank und der Input-Eingabe nicht das gleiche Format haben. Ich habe jedoch noch nicht herausgefunden, wie ich prüfen kann, welcher Typ bei der Variablen gespeichert ist.

Vielleicht kann mir ja jemand helfen:

1.) Wie prüfe ich den Typ einer Variablen, um zu sehen, ob die Typen zusammen passen?
2.) Wie kann ich hier ggf. die Typen so umwandeln, dass die If-Abfrage funktioniert?
3.) Wie kann ich bei der Select-Abfrage die Bedingung "where" mit einer Variablen eingrenzen?

Hier ist ein Auszug aus meiner bisherigen Programmierung:

import sqlite3
verbindung = sqlite3.connect("Vokabeln.db")
zeiger = verbindung.cursor()

inhaltdeutsch = zeiger.execute("select deutsch from Englischvokabeln where nr = 430")
vinhaltdeutsch = inhaltdeutsch.fetchone()
print(vinhaltdeutsch[0])

inhaltenglisch = zeiger.execute("select englisch from Englischvokabeln where nr = 430")
vinhaltenglisch = inhaltenglisch.fetchone()
print(vinhaltenglisch[0])


antwortenglisch = input("Wie lautet das englische Wort? ")

if inhaltenglisch == antwortenglisch:

print("Richtig!")

else:
print("Falsche Antwort!")

verbindung.commit()
verbindung.close()
Benutzeravatar
__blackjack__
User
Beiträge: 14047
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Linux-Fan: Den Typ eines Wertes kann man mit der `type()`-Funktion ermitteln. Und dann beispielsweise mit `print()` ausgeben um zu sehen was das für ein Typ ist. Vergleichen solltest Du den Typ nicht, denn Typvergleiche sind nur in sehr wenigen Fällen sinnvoll. Und wenn dann in der Regel auch mit `isinstance()` damit man auch abgeleitete Typen mit erwischt. Aber auch das ist nur selten sinnvoll und deutet oft auf ein Problem beim Entwurf der Lösung hin.

Die Datenbankabfragen liefern immer Tupel, auch wenn nur ein Wert pro Datensatz abgefragt wird. `input()` liefert eine Zeichenkette. Du machst bei der `print()`-Ausgabe ja schon das richtige: Du musst die Zeichenkette aus dem Tupel heraus holen. Und das am besten nicht beim `print()` oder beim Vergleich, sondern schon davor, direkt nach der Abfrage.

Und Du vergleichst auch das falsche. Die Rückgabe von `execute()` ist in der API-Spezifikation undefiniert. Den Wert `inhaltdeutsch` oder `inhaltenglisch` zu nennen ist also falsch. Den Wert überhaupt an einen Namen zu binden macht so überhaupt keinen Sinn. Dann kann auch das kryptische `v` aus `vinhaltdeutsch` und `vinhaltenglisch` verschwinden. Was sollte das denn überhaupt bedeuten? Und das sind jeweils zwei Worte die man durch einen Unterstrich trennen sollte.

Es macht keinen Sinn das in zwei Abfragen aufzuteilen wenn Du beide Werte für die gleiche `nr` haben willst.

Die Nummer kann man mit einem Platzhalter im SQL variabel machen und den Wert übergibt man dann über das zweite Argument von der `execute()`-Methode.

Bei `sqlite3` ist das Verbindungsobjekt ein Kontextmanager, dass heisst man kann das mit ``with`` verwenden. Den `Cursor` kann man mit `contextlib.closing()` zu einem Kontextmanager machen.

Eingerückt wird in Python vier Leerzeichen pro Ebene.

`zeiger` ist keine gute Übersetzung, denn das was *in* Datenbanken als CURSOR bezeichnet wird ist semantisch so etwas wie ein Zeiger, aber die `Cursor`-Objekte von der DB API V2 sind das nicht. Die haben nichts wirklich ”zeigerhaftes”. Ich würde auch von deutschsprachigen Namen abraten. Da kommt man viel zu oft zu dem Problem das Einzahl und Mehrzahl im Deutschen oft gleich heissen und dann muss man sich etwas für einzelne Elemente und Containertypen mit diesen Elementen überlegen.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import sqlite3
from contextlib import closing


def main():
    with sqlite3.connect("Vokabeln.db") as verbindung:
        with closing(verbindung.cursor()) as cursor:
            nummer = 430
            cursor.execute(
                "SELECT deutsch, englisch FROM englischvokabeln WHERE nr = ?",
                [nummer],
            )
            inhalt_deutsch, inhalt_englisch = cursor.fetchone()
            verbindung.commit()

    print(inhalt_deutsch)
    print(inhalt_englisch)

    antwort_englisch = input("Wie lautet das englische Wort? ")
    if inhalt_englisch == antwort_englisch:
        print("Richtig!")
    else:
        print("Falsche Antwort!")


if __name__ == "__main__":
    main()
Das mit dem einfach runterzählen wird nicht wirklich funktionieren, oder es ist halt nicht möglich, dass man jemals eine Vokabel und damit Nummer entfernt. Man würde da also eher alle Nummern sortiert abfragen und die dann der Reihe nach abarbeiten. Sortieren lassen kann sich die schon von der Datenbank. Auch absteigend, dann braucht man da nichts rückwärts iterieren.

Die Datenbankspalte `nr` würde traditionell auch eher `id` heissen. Und die Datenbankspalte würde ich in der Einzahl benennen, also `englischvokabel` beziehungsweise nur `vokabel`. Das `englisch` bringt nicht wirklich einen Mehrwert. Und falls man die Sprache mal irgendwann flexibler machen will, dann gehört diese Information nicht in Tabellen oder Spaltennamen, sondern als Datum in die Tabelle(n).
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Linux-Fan
User
Beiträge: 8
Registriert: Samstag 30. Mai 2020, 15:03

Vielen Dank für die ausführliche Antwort.
Die ganzen Hinweise haben mir sehr geholfen. Ich bin wieder einen Schritt weiter. Super!
Antworten