Wie aktualisiere ich den SQLAlchemy-Zeileneintrag?

136

Angenommen, die Tabelle hat drei Spalten : username, passwordund no_of_logins.

Wenn der Benutzer versucht, sich anzumelden, wird nach einem Eintrag mit einer Abfrage wie gesucht

user = User.query.filter_by(username=form.username.data).first()

Wenn das Passwort übereinstimmt, fährt er fort. Ich möchte zählen, wie oft sich der Benutzer angemeldet hat. Wenn er sich also erfolgreich anmeldet, möchte ich das no_of_loginsFeld erhöhen und in der Benutzertabelle speichern. Ich bin nicht sicher, wie ich die Update-Abfrage mit SqlAlchemy ausführen soll.

webminal.org
quelle

Antworten:

132
user.no_of_logins += 1
session.commit()
Denis
quelle
12
stackoverflow.com/a/2334917/125507 sagt, dass dies ein schlechter Weg ist, dies zu tun. Was ist auch, wenn die Zeile noch nicht existiert?
Endolith
5
user.no_of_logins += 1Kann laut Endolithen-Link Rennbedingungen schaffen. Verwenden Sie stattdessen user.no_of_logins = user.no_of_logins + 1. In SQL übersetzt wird der letztere richtige Weg : SET no_of_logins = no_of_logins + 1.
ChaimG
5
@ChaimG Ich denke, Sie meinten user.no_of_logins = User.no_of_logins + 1, oder verwenden Sie mit anderen Worten das instrumentierte Attribut des Modells, um einen SQL-Ausdruck zu erzeugen. Wie es ist, zeigt Ihr Kommentar zwei Möglichkeiten, um die gleiche Rennbedingung zu erzeugen.
Ilja Everilä
2
Sie sind nicht. Ersteres setzt das Attribut auf einen SQL-Ausdruck, letzteres führt eine direkte Addition in Python durch und führt das Rennen erneut ein.
Ilja Everilä
1
@jayrizzo Ich bin nicht so vertraut mit der Isolationsstufe von SERIALIZABLE-Transaktionen, aber meines Wissens würde es ermöglichen, die Addition in Python unter Verwendung der direkten Addition durchzuführen. Wenn es ein Rennen gibt, ist eine der Transaktionen erfolgreich und die anderen schlagen fehl und müssen es erneut versuchen (mit dem neuen Status der Datenbank). Aber ich könnte SERIALIZABLE falsch verstanden haben.
Ilja Everilä
342

Es gibt verschiedene Möglichkeiten zur UPDATEVerwendungsqlalchemy

1) user.no_of_logins += 1
   session.commit()

2) session.query().\
       filter(User.username == form.username.data).\
       update({"no_of_logins": (User.no_of_logins +1)})
   session.commit()

3) conn = engine.connect()
   stmt = User.update().\
       values(no_of_logins=(User.no_of_logins + 1)).\
       where(User.username == form.username.data)
   conn.execute(stmt)

4) setattr(user, 'no_of_logins', user.no_of_logins+1)
   session.commit()
Nima Soroush
quelle
3
Ich verwende setattr(query_result, key, value)für einige Updates in Flask SQLAlchemy, gefolgt von einem Commit. Gibt es einen Grund, dieses Muster abzuwerten?
Marc
2
@datamafia: Sie können verwenden setattr(query_result, key, value), was genau gleichbedeutend mit Schreiben istquery_result.key = value
Nima Soroush
Thx @NimaSoroush, das ist was ich tue und ja gleichwertig.
Marc
8
Ist es möglich, die Unterschiede zwischen diesen Fällen zu erklären? Vielen Dank!
Hatschepsut
1
@ Hatschepsut Ein Unterschied, auf den ich heute zwischen 4 und 1/2 gestoßen bin,
Vikas Prasad
13

Beispiele zur Klärung des wichtigen Problems in den Kommentaren der akzeptierten Antwort

Ich habe es nicht verstanden, bis ich selbst damit herumgespielt habe, also dachte ich, dass es auch andere geben würde, die verwirrt waren. Angenommen, Sie arbeiten an dem Benutzer, dessen id == 6und dessen no_of_logins == 30beim Start.

# 1 (bad)
user.no_of_logins += 1
# result: UPDATE user SET no_of_logins = 31 WHERE user.id = 6

# 2 (bad)
user.no_of_logins = user.no_of_logins + 1
# result: UPDATE user SET no_of_logins = 31 WHERE user.id = 6

# 3 (bad)
setattr(user, 'no_of_logins', user.no_of_logins + 1)
# result: UPDATE user SET no_of_logins = 31 WHERE user.id = 6

# 4 (ok)
user.no_of_logins = User.no_of_logins + 1
# result: UPDATE user SET no_of_logins = no_of_logins + 1 WHERE user.id = 6

# 5 (ok)
setattr(user, 'no_of_logins', User.no_of_logins + 1)
# result: UPDATE user SET no_of_logins = no_of_logins + 1 WHERE user.id = 6

Der Punkt

Indem Sie auf die Klasse anstelle der Instanz verweisen, können Sie SQLAlchemy beim Inkrementieren intelligenter machen und dies auf der Datenbankseite anstelle der Python-Seite veranlassen. Dies in der Datenbank zu tun ist besser, da es weniger anfällig für Datenbeschädigungen ist (z. B. versuchen zwei Clients gleichzeitig, mit einem Nettoergebnis von nur einem statt zwei Inkrementen zu erhöhen). Ich gehe davon aus, dass es möglich ist, das Inkrementieren in Python durchzuführen, wenn Sie Sperren setzen oder die Isolationsstufe erhöhen. Aber warum sollten Sie sich die Mühe machen, wenn Sie dies nicht müssen?

Eine Einschränkung

Wenn Sie zweimal über Code inkrementieren möchten, der SQL-ähnliche Effekte erzeugt SET no_of_logins = no_of_logins + 1, müssen Sie zwischen den Inkrementen ein Commit durchführen oder zumindest einen Flush durchführen. Andernfalls erhalten Sie insgesamt nur ein Inkrement:

# 6 (bad)
user.no_of_logins = User.no_of_logins + 1
user.no_of_logins = User.no_of_logins + 1
session.commit()
# result: UPDATE user SET no_of_logins = no_of_logins + 1 WHERE user.id = 6

# 7 (ok)
user.no_of_logins = User.no_of_logins + 1
session.flush()
# result: UPDATE user SET no_of_logins = no_of_logins + 1 WHERE user.id = 6
user.no_of_logins = User.no_of_logins + 1
session.commit()
# result: UPDATE user SET no_of_logins = no_of_logins + 1 WHERE user.id = 6
MarredCheese
quelle
Hallo, danke für deine Antwort. Anstelle einer int-Variablen versuche ich, eine String-Variable zu aktualisieren. Wie mache ich das? Ich benutze eine SQLite-Datenbank und die Variablen, die ich ändern möchte, sind in current_user, indem
ich
1
@danibilel Schauen Sie sich die Dokumentation an, die ziemlich gründlich ist. In diesem Teil des Tutorials werden die Zeichenfolgenfelder aktualisiert: docs.sqlalchemy.org/en/13/orm/… . Ansonsten schlage ich vor, dass Sie eine neue Frage stellen, die zeigt, woran Sie konkret festhalten.
MarredCheese
Vielen Dank für den Link. Nachdem ich den ganzen Tag mit der Suche verbracht habe, weiß ich, dass mein Problem bei db.session.commit () liegt. Die Änderungen in der Konsole bleiben nicht wie gewünscht erhalten. Ich habe ähnliche Fragen gefunden, aber keine Antworten gegeben Arbeit für mich, in der eigentlichen Web-App ist es noch schlimmer! Die Änderungen werden überhaupt nicht gespeichert. Entschuldigung für den langen Kommentar, aber ich kann aufgrund der Richtlinien der Website keine Frage hinzufügen. Jede Hilfe wäre willkommen.
Dani Bilel
4

Mit Hilfe der user=User.query.filter_by(username=form.username.data).first()Anweisung erhalten Sie den angegebenen Benutzer in userVariable.

Jetzt können Sie den Wert der neuen Objektvariablen wie ändern user.no_of_logins += 1und die Änderungen mit der sessionFestschreibungsmethode von ' speichern .

Nilesh
quelle
2

Ich habe einen Telegramm-Bot geschrieben und habe ein Problem mit Update-Zeilen. Verwenden Sie dieses Beispiel, wenn Sie über Model verfügen

def update_state(chat_id, state):
    try:
        value = Users.query.filter(Users.chat_id == str(chat_id)).first()
        value.state = str(state)
        db.session.flush()
        db.session.commit()
        #db.session.close()
    except:
        print('Error in def update_state')

Warum verwenden db.session.flush()? Deshalb >>> SQLAlchemy: Was ist der Unterschied zwischen flush () und commit ()?

Andrew Lutsyuk
quelle
7
Das Flush ist kurz vor dem Festschreiben vollständig redundant. Ein Commit wird immer implizit geleert. Wie viel in der flush()commit()
Frage / Antwort