Come aggiornare la voce della riga SQLAlchemy?


138

Si supponga tabella ha tre colonne: username, passworde no_of_logins.

Quando l'utente tenta di accedere, viene controllata una voce con una query simile

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

Se la password corrisponde, procede ulteriormente. Quello che vorrei fare è contare quante volte l'utente ha effettuato l'accesso. Pertanto, ogni volta che accede correttamente, vorrei incrementare il no_of_loginscampo e memorizzarlo nella tabella utente. Non sono sicuro di come eseguire la query di aggiornamento con SqlAlchemy.

Risposte:


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

12
stackoverflow.com/a/2334917/125507 afferma che questo è un brutto modo per farlo. Inoltre cosa succede se la riga non esiste ancora?
endolith

6
Secondo il link di endolith, user.no_of_logins += 1può creare condizioni di gara. Invece usa user.no_of_logins = user.no_of_logins + 1. Tradotto in sql il modo corretto quest'ultimo diventa: SET no_of_logins = no_of_logins + 1.
ChaimG,

5
@ChaimG Immagino volessi dire user.no_of_logins = User.no_of_logins + 1, o in altre parole usa l'attributo instrumentato del modello per produrre un'espressione SQL. Come è il tuo commento mostra 2 modi per produrre la stessa condizione di gara.
Ilja Everilä,

2
Non sono. Il primo imposta l'attributo su un'espressione SQL, il secondo esegue un'aggiunta sul posto in Python e introduce nuovamente la razza.
Ilja Everilä,

1
@jayrizzo Non ho molta familiarità con il livello di isolamento della transazione SERIALIZZABILE, ma a mio avviso ciò consentirebbe di eseguire l'aggiunta in Python usando l'aggiunta sul posto. Se c'è una gara, una delle transazioni avrà successo e le altre falliranno e dovranno riprovare (con il nuovo stato del DB). Ma potrei aver frainteso SERIALIZZABILE.
Ilja Everilä,

343

Esistono diversi modi per UPDATEutilizzaresqlalchemy

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()

3
Uso setattr(query_result, key, value)alcuni aggiornamenti in Flask SQLAlchemy seguiti da un commit. Qualche motivo per scontare questo modello?
Marc,

2
@datamafia: puoi usare setattr(query_result, key, value), che è esattamente equivalente alla scritturaquery_result.key = value
Nima Soroush,

Grazie @NimaSoroush, questo è quello che faccio e sì equivalente.
Marc,

8
È possibile spiegare le differenze tra questi casi? Grazie!
Hatshepsut

1
@Hatshepsut Una differenza che ho riscontrato oggi tra 4 e 1/2 è su: groups.google.com/forum/#!topic/sqlalchemy/wGUuAy27otM
Vikas Prasad

13

Esempi per chiarire l'importante questione nei commenti della risposta accettata

Non l'ho capito fino a quando non ci ho giocato da solo, quindi ho pensato che ci sarebbero stati anche altri confusi. Supponi di lavorare sull'utente di chi id == 6e di no_of_logins == 30chi inizi.

# 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

Il punto

Facendo riferimento alla classe anziché all'istanza, è possibile ottenere SQLAlchemy più intelligente sull'incremento, facendolo accadere sul lato database anziché sul lato Python. Farlo all'interno del database è meglio poiché è meno vulnerabile alla corruzione dei dati (ad esempio due client tentano di incrementare contemporaneamente con un risultato netto di un solo incremento invece di due). Suppongo che sia possibile eseguire l'incremento in Python se si impostano i blocchi o si aumenta il livello di isolamento, ma perché preoccuparsi se non è necessario?

Un avvertimento

Se hai intenzione di incrementare due volte tramite il codice che produce SQL like SET no_of_logins = no_of_logins + 1, allora dovrai eseguire il commit o almeno svuotare tra gli incrementi, altrimenti otterrai solo un incremento in totale:

# 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

Ciao, grazie per la tua risposta, invece di una variabile int sto provando ad aggiornare una variabile stringa, come posso farlo? sto usando un database sqlite e le variabili che voglio cambiare sono in current_user, attraverso l'invio di un modulo
dani bilel

1
@danibilel Dai un'occhiata alla documentazione, che è abbastanza approfondita. Questa parte del tutorial esamina l' aggiornamento dei campi stringa: docs.sqlalchemy.org/en/13/orm/… . Altrimenti, ti suggerisco di pubblicare una nuova domanda che mostri su cosa sei bloccato.
MarredCheese

Grazie per il link, dopo aver trascorso l'intera giornata a cercare, so che il mio problema è con db.session.commit (), non persiste i cambiamenti nella console come dovrebbe, ho trovato domande simili ma fornito risposte non lavorare per me, nella web app è ancora peggio! le modifiche non vengono affatto salvate, scusate il lungo commento ma non posso aggiungere una domanda a causa della politica del sito Web, qualsiasi aiuto sarebbe apprezzato.
dani bilel,

4

Con l'aiuto di user=User.query.filter_by(username=form.username.data).first()statement otterrai l'utente specificato in uservariabile.

Ora puoi cambiare il valore della nuova variabile oggetto come user.no_of_logins += 1e salvare le modifiche con il sessionmetodo di commit.


2

Ho scritto un bot di telegrammi e ho qualche problema con le righe di aggiornamento. Usa questo esempio, se hai Model

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')

Perché l'uso db.session.flush()? Ecco perché >>> SQLAlchemy: qual è la differenza tra flush () e commit ()?


7
Il flush è completamente ridondante appena prima del commit. Un commit arrossisce sempre implicitamente. Come si dice nel Q / A che hai collegato: " flush()viene sempre chiamato come parte di una chiamata a commit()"
Ilja Everilä
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.