Blob Update / Insert

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
locust
User
Beiträge: 4
Registriert: Donnerstag 23. Juni 2016, 08:25

Freitag 20. März 2020, 15:35

Hallo, ich versuche anhand einer Produktliste die passenden Fotos in einer sqlite Datenbank anzupassen.
Irgendwo steckt in meiner Logik jedoch der Wurm drin. Nur beim ersten Datensatz wird der BLOB hinterlegt.
Keine Fehlermeldungern etc. Hat jemand eine Idee warum der for loop einfach aufhört ? Wenn ich den code nur bis
print (filename) laufen lasse, werden alle angezeigt......

Output:
110000002060
/Users/products/original/110000002060/110000002060.PNG
Uploaded

Code: Alles auswählen

try:
	result = cur.execute("""Select "f_reference" from Produkte;""")
	for row in result:
		for val in row:
			print(val)
			path = "/Users/products/original/" + val + "/*.*"
			for filename in glob.glob(path):
				print(filename)
				file = open(filename, "rb")
				blobData = file.read()
				try:
					cur.execute(
						'UPDATE Produkte SET "f_pic_lg" = ? where "f_reference"=?',
						(blobData, val),
					)
					print("Uploaded")
					con.commit()
					file.close()
				except sqlite3.Error as e:
					print("Database error: %s" % e)
				
except sqlite3.Error as e:
	print("Database error: %s" % e)
Benutzeravatar
sparrow
User
Beiträge: 1756
Registriert: Freitag 17. April 2009, 10:28

Freitag 20. März 2020, 15:50

Eingerückt wird in Python mit 4 Leerzeichen, nicht mit Tabstops.
Zeichenketten stückelt man nicht + zusammen. Erst recht nicht Pfade. Dafür gibt es das Modul pathlib.
Benenne Variablen aussagekräftig. "val" ist das nicht.
Öffne Dateien mit dem with-Statement, damit du das Schließen sparen kannst. Im Moment wird da nämlich nichts geschlossen, wenn es einen sqlite-Fehler gibt.

Bist du dir sicher, dass es mehr als einen Eintrag in Produkte gibt?
Ansonsten ist es vielleicht nicht beste Idee über einen Cursor zu iterieren und dann innerhalb der Iteration auf dem selben Cursor ein Execute durchzuführen.
Benutzeravatar
__blackjack__
User
Beiträge: 5460
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Freitag 20. März 2020, 16:16

@locust: Statt `con` und `cur` sollte man auch `connection` und `cursor` schreiben. Kryptische Abkürzungen erschweren das Lesen und damit das Verständnis nur unnötig.

Die ``for val in row:``-Schleife ist unsinnig. In `row` ist *immer* genau *ein* Element, da macht eine Schleife überhaupt gar keinen Sinn.

Und auch die Schleife über die Dateinamen macht keinen Sinn weil da ja jede Datei die vorhergehende in der Datenbank überschreibt. Man kann pro `f_reference` ja nur einen Wert setzen.

Aus den `print()`-Aufrufen würde ich Logging machen. Und das "Uploaded" gehört *hinter* das `commit()`, denn das könnte ja noch fehlschlagen. Wirklich sicher in der DB ist es erst nach diesem Aufruf.

``%`` zur Formatierung von Werten in Zeichenketten ist veraltet.

Die Spaltennamen in der Datenbank sind komisch. Was soll der kryptische `f_`-Präfix? Und auch hier gilt: keine Abkürzungen. `large_picture` ist deutlich verständlicher als `f_pic_lg`.

Ungetestet:

Code: Alles auswählen

    try:
        for reference, in cursor.execute("SELECT f_reference FROM Produkte"):
            print(reference)
            path = Path("/Users/products/original", reference)
            file_path = next(path.glob("*.*"), None)
            if file_path is not None:
                print(file_path)
                image_data = file_path.read_bytes()
                try:
                    connection.execute(
                        "UPDATE Produkte SET f_pic_lg=? WHERE f_reference=?",
                        (image_data, reference),
                    )
                    connection.commit()
                    print("Uploaded")
                except sqlite3.Error as error:
                    print("Database error:", error)
    except sqlite3.Error as error:
        print("Database error:", error)
long long ago; /* in a galaxy far far away */
locust
User
Beiträge: 4
Registriert: Donnerstag 23. Juni 2016, 08:25

Freitag 20. März 2020, 19:17

Super, danke für die schnelle Hilfe.

Zu euren Kommentaren,
die Print-Anweisungen waren nur zum "debuggen" und werden nachdem es funktioniert wieder gelöscht,
der String-Pfad zur Datei dient ebenfalls nur in der Testumgebung. Das eigentliche Program hat eine Qt Oberfläche und lässt den User den Pfad auswählen,
die Tabellennamen stammen aus einer WaWi und sind nicht änderbar.
Bzgl. der Variablen sehe ich eigentlich kein Problem row und val werden selbst in Büchern benutzt und sind schnell nachzuvollziehen.

:)
Benutzeravatar
__blackjack__
User
Beiträge: 5460
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Freitag 20. März 2020, 20:39

@locust: Beim Logging gibt es ein DEBUG-Level das genau für Debug-Ausgaben gedacht ist. Vorteil ist, dass man das nicht wieder entfernen muss. Einfach das Level höher als DEBUG setzten und schon wird da nichts mehr ausgegeben. Und wenn etwas nicht so funktioniert wie es soll, kann man das auch ganz einfach wieder aktivieren.

Beim Dateipfad ging es nicht nur darum das es eine Zeichenkette ist, sondern vor allem das man Pfadangaben nicht so behandeln kann ohne das dadurch Fehler entstehen können. Ein Pfad muss nicht mit einem Pfadtrenner enden und der Pfadtrenner kann unter verschiedenen Betriebssystemen unterschiedlich sein. Deshalb kann man das nicht einfach wie normale Zeichenketten behandeln.

In Büchern werden auch Namen wie `foo` und `bar` benutzt, oder `spam` und `parrot`. Der Name `val` sagt halt nichts aus und ist eine nicht allgemein bekannte Abkürzung und damit ist das eben ein schlechter Name. Selbst `value` ist ein schlechter Name wenn das nicht für einen allgemeinen „Wert“ steht, denn das ist letztlich ein Namen den man für *alles* nehmen kann, denn alles was man an einen Namen binden kann, letztlich ein Wert ist.

In Büchern besteht oft auch das Problem das der Code kürzer als 80 Zeichen pro Zeile sein muss, damit keine Umbrüche entstehen. Ausserdem muss man zwischen Beispielen in der interaktiven Shell und Quelltext für Modulen unterscheiden. In der Shell, wo ja immer nur sehr kurze Beispiele durchgespielt werden, gibt es so wenige Namen, dass man da nicht ganz so gute Namen braucht. Die Wichtigkeit eines guten Namens steigt mit der Reichweite seines Gültigkeitsbereichs.

Gegen den Namen `row` gab es nichts einzuwenden, sondern das über eine Sequenz die immer genau einen Wert enthält, eine Schleife unsinnig und irreführend ist. Und man kann diesen einzelnen Wert halt auch gleich in der Schleife auf einen Namen entpacken, womit man den Zwischenschritt über den Namen `row` nicht mehr benötigt.
long long ago; /* in a galaxy far far away */
Antworten