2015-11-18 17 views
8

Mi chiedo in che modo SQLAlchemy tiene traccia delle modifiche apportate al di fuori di SQLAlchemy (modifica manuale ad esempio)?Come si modifica il database di SQLAlchemy?

Fino ad ora, inserivo db.session.commit() prima di ogni valore che può essere modificato all'esterno di SQLAlchemy. È una cattiva pratica? Se sì, c'è un modo migliore per assicurarmi che avrò l'ultimo valore? In realtà ho creato un piccolo script qui sotto per verificarlo e apparentemente, SQLAlchemy è in grado di rilevare le modifiche esterne senza che venga chiamato ogni volta db.session.commit().

Grazie,

P.S: Ho molta voglia di capire come tutte le magie accadono dietro il lavoro SQLAlchemy. Qualcuno ha un puntatore ad alcuni documenti che spiegano il lavoro dietro le quinte di SQLAlchemy?

import os 

from flask import Flask 
from flask_sqlalchemy import SQLAlchemy 

app = Flask(__name__) 

# Use SQLlite so this example can be run anywhere. 
# On Mysql, the same behaviour is observed 
basedir = os.path.abspath(os.path.dirname(__file__)) 
db_path = os.path.join(basedir, "app.db") 
app.config["SQLALCHEMY_DATABASE_URI"] = 'sqlite:///' + db_path 
db = SQLAlchemy(app) 


# A small class to use in the test 
class User(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.String(100)) 


# Create all the tables and a fake data 
db.create_all() 
user = User(name="old name") 
db.session.add(user) 
db.session.commit() 


@app.route('/') 
def index(): 
    """The scenario: the first request returns "old name" as expected. 
    Then, I modify the name of User:1 to "new name" directly on the database. 
    On the next request, "new name" will be returned. 
    My question is: how SQLAlchemy knows that the value has been changed? 
    """ 

    # Before, I always use db.session.commit() 
    # to make sure that the latest value is fetched. 
    # Without db.session.commit(), 
    # SQLAlchemy still can track change made on User.name 
    # print "refresh db" 
    # db.session.commit() 

    u = User.query.filter_by(id=1).first() 
    return u.name 


app.run(debug=True) 
+0

I documenti SQLAlchemy sono molto, * molto * completi e menzionano anche le risorse esterne. Hai considerato di leggere quelli per capire come funziona? – davidism

+0

Inoltre, non è chiaro quale problema si ha con il codice che hai postato. Si prega di [modificare] per creare un [mcve] che descrive il problema * nel post stesso *. – davidism

+0

hey grazie per il feedback. Ho provato a cercare in SQLAlchemy doc ma non ho trovato le parti che descrivono il meccanismo dietro questa "magia". Forse questa è una tecnica utilizzata da un sacco di ORM, quindi SQLAlchemy non ha bisogno di indicarlo? – Son

risposta

4

Il "cache" di una sessione è un dict nella sua identity_map (session.identity_map.dict) che memorizza nella cache solo gli oggetti per il tempo di "una singola transazione commerciale", come risposto qui https://stackoverflow.com/a/5869795.

Per diverse richieste del server, si ha diversa identity_map. Non è un oggetto condiviso.

Nel vostro scenario, avete richiesto al server 2 tempi separati. La seconda volta, identity_map è una nuova (è possibile controllarla facilmente stampando il suo puntatore) e non ha nulla nella cache. Di conseguenza, la sessione richiederà il database e otterrà la risposta aggiornata. Non "traccia il cambiamento" come potresti pensare.

Così, alla tua domanda, non è necessario fare session.commit() prima di una query, se non avete fatto una query per lo stesso oggetto nella richiesta stesso server.

Spero che aiuti.

+0

"I record vengono mantenuti nella mappa dell'identità per una singola transazione commerciale. Ciò significa che, indipendentemente da come è configurato il server web, probabilmente non verranno conservati per più di una richiesta (o memorizzati nella sessione)." citato da http://stackoverflow.com/questions/5869514/sqlalchemy-identity-map-question/5869795#5869795 – Son