PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Python-Liste in PostgreSQL-Array

Beachten Sie das bei psycopg2 Sie müssen keine Zeichenfolgenverarbeitung für Arrays durchführen. Dies wird als schlechte Praxis angesehen, da es fehleranfällig ist und im schlimmsten Fall dazu führen kann, dass sich Injektionsangriffe eröffnen! Sie sollten immer gebundene Parameter verwenden. Im folgenden Code erstelle ich eine neue Tabelle mit nur einer Spalte vom Typ TEXT[] (wie in Ihrer ursprünglichen Frage). Dann werde ich eine neue Zeile hinzufügen und alle aktualisieren. Sie sehen also sowohl ein INSERT und UPDATE Betrieb (obwohl beide ziemlich identisch sind).

Es gibt jedoch einen Python-Fehler, wenn Sie mit nur einem Wert aktualisieren:cur.execute erwartet als erstes Argument das SQL-Statement und ein iterable enthält die zu bindenden Parameter als zweites Argument. Das Folgende wird nicht Arbeit:

from psycopg2 import connect

conn = connect('dbname=exhuma')
cur = conn.cursor()
stmt = 'UPDATE foo SET example_value=%s'
new_values = ['a', 'b', 'c']
cur.execute(stmt, (new_values))
conn.commit()

Der Grund dafür ist, dass (new_values) wird von Python als new_values gesehen (Die Klammern werden in diesem Fall weggelassen, sie werden nicht als Tupel angesehen). Dies führt zu dem Fehler, dass Sie 3 Werte angeben ('a' , 'b' und 'c' ) als zu bindende Werte, aber es gibt nur einen Platzhalter (%s ) in der Abfrage. Stattdessen müssen Sie es wie folgt angeben (beachten Sie das hinzugefügte Komma am Ende):

from psycopg2 import connect

conn = connect('dbname=exhuma')
cur = conn.cursor()
stmt = 'UPDATE foo SET example_value=%s'
new_values = ['a', 'b', 'c']
cur.execute(stmt, (new_values,))
conn.commit()

Dadurch sieht Python (new_values,) als Tupel (das iterierbar ist) mit einem Element, das mit den Platzhaltern der Abfrage übereinstimmt. Eine ausführlichere Erklärung des nachgestellten Kommas finden Sie in der offiziellen Dokumentation zu Tupeln.

Alternativ könntest du auch [new_values] schreiben statt (new_values,) , aber - meiner Meinung nach - (new_values,) ist sauberer, da Tupel unveränderlich sind, während Listen veränderlich sind.

Hier ist die Tabelle, mit der ich getestet habe:

CREATE TABLE foo (
    values TEXT[]
);

Und hier ist Python-Code, der Werte sowohl einfügt als auch aktualisiert:

from psycopg2 import connect


conn = connect('dbname=exhuma')
cur = conn.cursor()

cur.execute('INSERT INTO foo VALUES (%s)', (['a', 'b'], ))

print('>>> Before update')
cur.execute('SELECT * FROM foo')
for row in cur:
    print(type(row[0]), repr(row[0]))

print('>>> After update')

cur.execute('UPDATE foo SET example_values = %s',
            (['new', 'updated', 'values'],))

cur.execute('SELECT * FROM foo')
for row in cur:
    print(type(row[0]), repr(row[0]))

cur.close()
conn.commit()
conn.close()

Bei jeder Ausführung fügt der Code eine neue Zeile mit denselben Array-Werten ein und führt dann eine Aktualisierung ohne WHERE aus -Klausel, sodass alle Werte aktualisiert werden. Nach einigen Ausführungen ergibt dies die folgende Ausgabe:

>>> Before update
(<type 'list'>, "['new', 'updated', 'values']")
(<type 'list'>, "['new', 'updated', 'values']")
(<type 'list'>, "['new', 'updated', 'values']")
(<type 'list'>, "['new', 'updated', 'values']")
(<type 'list'>, "['new', 'updated', 'values']")
(<type 'list'>, "['a', 'b']")
>>> After update
(<type 'list'>, "['new', 'updated', 'values']")
(<type 'list'>, "['new', 'updated', 'values']")
(<type 'list'>, "['new', 'updated', 'values']")
(<type 'list'>, "['new', 'updated', 'values']")
(<type 'list'>, "['new', 'updated', 'values']")
(<type 'list'>, "['new', 'updated', 'values']")