Problem beim Code (Update der Datenbank Einträge)

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
Kaan38.png
User
Beiträge: 9
Registriert: Samstag 20. Februar 2021, 15:56

Hallo, ich arbeite an einer To do Liste für mein Abschlussprojekt aber habe einen Fehler in der Datenbank. Ich suche den Fehler seit 2 Tagen aber konnte Ihn nicht finden. Mir wird angezeigt, dass etwas in def update falsch ist aber ich finde es irgendwie nicht.
Könnte mir einer weiterhelfen?

from tkinter import *
import sqlite3


root = Tk()
root.title('Projektarbeit')
root.geometry("450x700")
conn = sqlite3.connect("ToDoList.db")

cur = conn.cursor()

cur.execute("CREATE TABLE IF NOT EXISTS Tasks (aufgabe TEXT , beschreibung TEXT , kategorie TEXT)")

def delete():
conn = sqlite3.connect("ToDoList.db")
cur = conn.cursor()

cur.execute("DELETE from Tasks WHERE oid= " + loeschen_box.get())

conn.commit()
conn.close()

def update():
# database connect
conn = sqlite3.connect("ToDoList.db")
cur = conn.cursor()

record_id = loeschen_box.get()

cur.execute("""UPDATE Tasks SET
aufgabe = :aufgabe,
beschreibung = :beschreibung,
kategorie = :kategorie

WHERE oid = :oid""",
{
'aufgabe': aufgabe_editor.get(),
'beschreibung': beschreibung_editor.get(),
'kategorie': kategorie_editor.get(),
'oid': record_id
})

conn.commit()
conn.close()

editor.destroy()

#CREATE EDIT
def edit():
global editor
editor = Tk()
editor.title('Projektarbeit (Task Edits)')
editor.geometry("450x700")

#database connect
conn = sqlite3.connect("ToDoList.db")
cur = conn.cursor()

record_id = loeschen_box.get()
cur.execute("SELECT * FROM Tasks WHERE oid = " + record_id)
records = cur.fetchall()

conn.commit()
conn.close()


#GLOBAL TEXT BOX NAMES
global aufgabe_editor
global beschreibung_editor
global kategorie_editor


# TEXT BOX LABEL
aufgabe_label = Label(editor, text="Aufgabe")
aufgabe_label.grid(row=2, column=0)
beschreibung_label = Label(editor, text="Beschreibung")
beschreibung_label.grid(row=3, column=0)
kategorie_label = Label(editor, text="Kategorie")
kategorie_label.grid(row=4, column=0)

# TEXT BOX
aufgabe_editor = Entry(editor, width=30)
aufgabe_editor.grid(row=2, column=1, padx=20)
beschreibung_editor = Entry(editor, width=30)
beschreibung_editor.grid(row=3, column=1, padx=20)
kategorie_editor = Entry(editor, width=30)
kategorie_editor.grid(row=4, column=1, padx=20)

# loop results
for record in records:
aufgabe_editor.insert(0, record[0])
beschreibung_editor.insert(0, record[1])
kategorie_editor.insert(0, record[2])

# Edit Save
edit_btn = Button(editor, text="Speichern", command=update)
edit_btn.grid(pady=2, ipady=2, ipadx=14, column=1, columnspan=1)


# CREATE SUBMIT FUNC FOR DB
def submit():
conn=sqlite3.connect("ToDoList.db")
cur = conn.cursor()

#INSERT INTO TABLE
cur.execute("INSERT INTO Tasks VALUES(:aufgabe, :beschreibung, :kategorie)",
{
'aufgabe': aufgabe.get(),
'beschreibung': beschreibung.get(),
'kategorie': kategorie.get()
})
conn.commit()
conn.close()

#clear text boxes
aufgabe.delete(0,END)
beschreibung.delete(0,END)
kategorie.delete(0,END)

#QUERY FUNKTION
def query():
conn = sqlite3.connect("ToDoList.db")
cur = conn.cursor()

#Query database
cur.execute("SELECT * , oid FROM Tasks")
records = cur.fetchall()
# print(records)

#Loop results
print_records = ''
for record in records:
print_records += str(record[0]) + " " + str(record[1]) + " " + str(record[2]) + " " + str(record[3]) + "\n"


query_label = Label(root, text=print_records)
query_label.grid(row=12, column=1)

conn.commit()
conn.close()


#TEXT BOX LABEL
aufgabe_label = Label(root, text="Aufgabe")
aufgabe_label.grid(row=2, column=0)
beschreibung_label = Label(root, text="Beschreibung")
beschreibung_label.grid(row=3, column=0)
kategorie_label = Label(root, text="Kategorie")
kategorie_label.grid(row=4, column=0)


#TEXT BOX
aufgabe=Entry(root, width=30)
aufgabe.grid(row=2, column=1, padx=20)
beschreibung=Entry(root, width=30)
beschreibung.grid(row=3, column=1, padx=20)
kategorie = Entry(root, width=30)
kategorie.grid(row=4, column=1, padx=20)


#CREATE SUBMIT BUTTON
submit_btn = Button(root, text="Hinzufügen", command=submit)
submit_btn.grid(pady=8, padx=1, ipady=2, ipadx=11, column=1, columnspan=1)

#QUERY BUTTON
query_btn = Button(root, text="Zeige Einträge", command=query)
query_btn.grid(pady=5, padx=10, ipady=2, ipadx=2,column=1, columnspan=1)


#Delete Tasks
loeschen_box_label = Label(root, text="Auswählen (ID Nummer)")
loeschen_box_label.grid(row=14, column=1, columnspan=1,pady=5)

loeschen_box = Entry(root, width=30)
loeschen_box.grid(row=15, column=1)

delete_btn = Button(root, text="Löschen", command=delete)
delete_btn.grid(pady=2, padx=1, ipady=2, ipadx=20,column=1, columnspan=1)

#Edit
edit_btn = Button(root, text="Bearbeiten", command=edit)
edit_btn.grid(pady=2, ipady=2, ipadx=14, column=1, columnspan=1)


conn.commit()
conn.close()
Benutzeravatar
Dennis89
User
Beiträge: 1123
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

dein Code entspricht nicht gerade den Konventionen eines Python-Programms, dadurch ist es unter anderem schwerer zu lesen.
Kannst du mal die vollständige Fehlermeldung posten?

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Da ist so einige falsch. Man benutzt keine *-Importe, sondern importiert Namen explizit. Bei Tkinter wird üblicherweise mit `import tkinter as tk` importiert, und alle Namen per tk.xyz angesprochen.
Auf oberster Ebene darf kein ausführbarer Code stehen, sondern alles muß in Funktionen stehen, damit man nicht aus Versehen globale Variablen benutzt, und dass man auch nicht Code zwischen Funktionen zusammensuchen muß.

Dann dürfen Funktionen nicht einfach auf globale Variablen zugreifen, sondern sie müssen alles was sie brauchen per Argumente bekommen.

Niemals Werte in SQL-Statements hineinformatieren, oder gar Strings per + zusammenstückeln. Das führt zu Sicherheitslücken und schwer zu findende Fehlern. Benutze Platzhalter. Aber wie kommst Du darauf, dass :aufgabe für Sqlite der richtige Platzhalter wäre. Lies in der Dokumentation nach, wie es richtig geht.

Es darf nur ein einziges Exemplar von Tk im ganzen Programm geben. In `edit` erzeugst Du ein zweites. Aber zweite Fenster erzeugt man mit Toplevel.
Wenn man eine Query hat, die exakt ein Element liefert, dann benutzt man nicht fetchall sondern fetchone.

In `query` wird jedesmal ein neues Label erzeugt, das sollte nicht sein. Man baut einmal am Anfang alle Widgets auf, und ändert die dann nur noch.

Und ganz zum Schluß fehlt ein root.mainloop.

Jedes nicht triviale GUI-Programm braucht eigentlich Klassendefinitionen.

Nicht alles muß man an Variablen binden. Wenn ein Label nicht weiter gebraucht wird, dann braucht es keine Variable. Die Aufteilung in Labels und Editboxen ist schlecht, weil jeweils ein Label zu einer Box gehört.

Was noch schlecht ist, ist das Benutzen der impliziten Datenbank-ID oid, mache das explizit.
Für jede Aktion die Datenbank neu zu öffnen ist nicht so sinnvoll, eigentlich öffnet man einmal die Datenbank und benutzt die in verschiedenen Funktionen.

Code: Alles auswählen

from functools import partial
import tkinter as tk
import sqlite3


def initalize_database():
    conn = sqlite3.connect("ToDoList.db")
    cur = conn.cursor()
    cur.execute("CREATE TABLE IF NOT EXISTS Tasks (aufgabe TEXT , beschreibung TEXT , kategorie TEXT)")
    conn.commit()
    conn.close()


def delete(loeschen_box):
    conn = sqlite3.connect("ToDoList.db")
    cur = conn.cursor()
    cur.execute("DELETE from Tasks WHERE oid= ?", [loeschen_box.get()])
    conn.commit()
    conn.close()


def update(editor, record_id, aufgabe_editor, beschreibung_editor, kategorie_editor):
    conn = sqlite3.connect("ToDoList.db")
    cur = conn.cursor()
    cur.execute("""UPDATE Tasks SET
        aufgabe = ?, beschreibung = ?, kategorie = ?
        WHERE oid = ?""",
        [aufgabe_editor.get(), beschreibung_editor.get(), kategorie_editor.get(), record_id])
    conn.commit()
    conn.close()
    editor.destroy()


def edit(loeschen_box):
    editor = tk.Toplevel()
    editor.title('Projektarbeit (Task Edits)')
    record_id = loeschen_box.get()

    conn = sqlite3.connect("ToDoList.db")
    cur = conn.cursor()
    cur.execute("SELECT aufgabe, beschreibung, kategorie FROM Tasks WHERE oid = ?", [record_id])
    record = cur.fetchone()
    conn.commit()
    conn.close()

    tk.Label(editor, text="Aufgabe").grid(row=2, column=0)
    aufgabe_editor = tk.Entry(editor, width=30)
    aufgabe_editor.grid(row=2, column=1, padx=20)

    tk.Label(editor, text="Beschreibung").grid(row=3, column=0)
    beschreibung_editor = tk.Entry(editor, width=30)
    beschreibung_editor.grid(row=3, column=1, padx=20)

    tk.Label(editor, text="Kategorie").grid(row=4, column=0)
    kategorie_editor = tk.Entry(editor, width=30)
    kategorie_editor.grid(row=4, column=1, padx=20)

    aufgabe, beschreibung, kategorie = record
    aufgabe_editor.insert(0, aufgabe)
    beschreibung_editor.insert(0, beschreibung)
    kategorie_editor.insert(0, kategorie)

    tk.Button(editor, text="Speichern",
        command=partial(update, editor, record_id, aufgabe_editor, beschreibung_editor, kategorie_editor)
    ).grid(pady=2, ipady=2, ipadx=14, column=1, columnspan=1)


def submit(aufgabe, beschreibung, kategorie):
    conn=sqlite3.connect("ToDoList.db")
    cur = conn.cursor()
    cur.execute("INSERT INTO Tasks VALUES(?, ?, ?)",
        [aufgabe.get(), beschreibung.get(), kategorie.get()])
    conn.commit()
    conn.close()

    #clear text boxes
    aufgabe.delete(0, tk.END)
    beschreibung.delete(0, tk.END)
    kategorie.delete(0, tk.END)


def query(query_label):
    conn = sqlite3.connect("ToDoList.db")
    cur = conn.cursor()

    #Query database
    cur.execute("SELECT aufgabe, beschreibung, kategorie, oid FROM Tasks")

    records = []
    for aufgabe, beschreibung, kategorie, oid in cur:
        records.append(f"{aufgabe}  {beschreibung}  {kategorie}    {oid}")
    query_label['text'] = '\n'.join(records)
    conn.commit()
    conn.close()


def main():
    initalize_database()

    root = tk.Tk()
    root.title('Projektarbeit')

    tk.Label(root, text="Aufgabe").grid(row=2, column=0)
    aufgabe = tk.Entry(root, width=30)
    aufgabe.grid(row=2, column=1, padx=20)

    tk.Label(root, text="Beschreibung").grid(row=3, column=0)
    beschreibung = tk.Entry(root, width=30)
    beschreibung.grid(row=3, column=1, padx=20)

    tk.Label(root, text="Kategorie").grid(row=4, column=0)
    kategorie = tk.Entry(root, width=30)
    kategorie.grid(row=4, column=1, padx=20)


    tk.Button(root, text="Hinzufügen",
        command=partial(submit, aufgabe, beschreibung, kategorie)
    ).grid(pady=8, padx=1, ipady=2, ipadx=11, column=1, columnspan=1)

    query_label = tk.Label(root, text="")
    query_label.grid(row=12, column=1)
    tk.Button(root, text="Zeige Einträge",
        command=partial(query, query_label)
    ).grid(pady=5, padx=10, ipady=2, ipadx=2,column=1, columnspan=1)

    tk.Label(root, text="Auswählen (ID Nummer)").grid(row=14, column=1, columnspan=1,pady=5)

    loeschen_box = tk.Entry(root, width=30)
    loeschen_box.grid(row=15, column=1)

    tk.Button(root, text="Löschen",
        command=partial(delete, loeschen_box),
    ).grid(pady=2, padx=1, ipady=2, ipadx=20,column=1, columnspan=1)

    tk.Button(root, text="Bearbeiten",
        command=partial(edit, loeschen_box)
    ).grid(pady=2, ipady=2, ipadx=14, column=1, columnspan=1)

    root.mainloop()

if __name__ == "__main__":
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sirius3: Die Platzhalter sind schon richtig so. "?" ist für unbenannte ":name" für benannte Platzhalter. Ein weiterer blöder Punkt an der DB-API V2, das man da nur *einen* Stil angeben kann, viele Datenbanken/Module aber etwas für beides anbieten. SQLite3 netterweise das was Standard-SQL an der Stelle für diese beiden Varianten verwendet. Die Dokumentation zeigt beides: https://docs.python.org/3/library/sqlit ... or.execute
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Kaan38.png
User
Beiträge: 9
Registriert: Samstag 20. Februar 2021, 15:56

Hallo, ich habe es so ausprobiert wie Sie es gesagt haben, aber ich habe den selben Fehler wieder bekommen.
Bei mir wird immer angezeigt, dass die Spalte Aufgabe in meiner Datenbank nicht existiert.
Die Fehlermeldung:
line 81, in edit
cur.execute("SELECT aufgabe, beschreibung, kategorie FROM Tasks WHERE oid = ?", [record_id])
sqlite3.OperationalError: no such column: aufgabe
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie sieht denn Deine Datenbanktabelle aus?
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Kaan38.png: Dann wird es in der Tabelle `tasks` die Spalte `aufgabe` halt nicht geben. Eventuell ist die "ToDoList.db" nicht die Datei die Du denkst die es wäre? Wo die liegt hängt ja davon ab, was das aktuelle Arbeitsverzeichnis beim starten des Programms ist, und das wiederum hängt davon ab von wo und/oder wie das Programm gestartet wird. Es wäre vielleicht sinnvoller diesen Dateipfad etwas fester zu machen. Entweder wirklich einen absoluten Pfad angeben, oder den relativ zu etwas mehr oder weniger festen zu machen. Heimatverzeichnis des Benutzers der das ausführt. Oder das Verzeichnis in dem die Python-Datei liegt.

Man sollte die Information auch nicht so oft im Quelltext stehen haben. Das wäre ein typischer Fall für etwas das man als Konstante definiert. Und auch einiges an Code um diesen Dateinamen wiederholt sich oft im Quelltext — das sollte auch nicht sein, weil es das DRY-Prinzip verletzt.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Kaan38.png
User
Beiträge: 9
Registriert: Samstag 20. Februar 2021, 15:56

@__blackjack__ Vielen dank für deine Hilfe ich habe nochmals eine andere Datenbank erstellt und diesmal hat es funktioniert. Ich weiß zwar nicht ganz wieso es nicht davor funktioniert hat aber nun funktioniert es.

Hätte einer eine Idee wie man Gruppieren und Wiederherstellen einzelner To-Do-Einträge ermöglichen kann?
Antworten