2017-05-25 3 views
0

Ich habe einen Endpunkt, um ein Objekt aus meiner Datenbank zu löschen. Ich lösche es mit dem folgenden Code:SQLAlchemy immer noch in der Lage, ein Objekt aus der Sitzung nach dem Löschen zu bekommen

my_object = Object.query.get(id) 
db.session.delete(my_object) 
db.session.commit() 
return json.dumps({'success': True} 

ich einen API-Test habe den Endpunkt zu testen, wo ich ein Objekt erstellen und dann den Endpunkt verwenden, um es zu löschen. Ich versuche zu behaupten, dass nach der Löschung geschieht es nicht in der Datenbank ist.

my_object = MyObject() 
db.session.add(my_object) 
db.session.commit() 
response = requests.delete('{}/my-object/{}'.format(
    os.environ.get('MY_URL'), 
    my_object.id 
)) 
self.assertTrue(response.json()['success']) // this passes 
self.assertEqual(200, response.status_code) // this passes 
result = db.session.query(MyObject).get(my_object.id) 
print(result.id) // prints the id of the job even though it is deleted from the database 

Ich denke, das bis zu einem gewissen SQLAlchemy Sitzung Caching verwendet ist. Ich habe db.session.flush(), db.session.expire_all() vergeblich versucht. Das Objekt wird tatsächlich aus der Datenbank gelöscht. So würde ich erwarten, dass das Abfrageergebnis None ist.

Ich sehe das in der docs aber habe nicht meinen ganzen Kopf darum gewickelt. when to commit and close a session

Danke für jede Hilfe.

+0

Haben Sie versucht, vor der Abfrage aber nach dem Löschen zu committen? Oder vielleicht wäre das Öffnen einer neuen Sitzung noch sauberer. – 9000

+1

Zwei Fragen, die helfen könnten, dies einzugrenzen: Führen Sie Ihren Server im selben Prozess? Verwendet Ihr Testframework Transaktionen, um die Datenbank nach jedem Test in den vorherigen Zustand zurückzusetzen? –

+0

@ 9000 Ich bin mir nicht sicher, ob ich die erste Frage verstehe. Ich kann versuchen, eine neue Sitzung zu öffnen und zu sehen, ob das funktioniert. – brandonbanks

Antwort

2

Also in Ihrem Test-Code, fügen Sie das Objekt zu einer Sitzung und Commit es. Es wird in der Datenbank gespeichert und ist die Identitätskarte Ihrer Sitzung.

Dann drücken Sie Ihre App, es hat eine eigene Sitzung. Es löscht das Objekt und Commits, jetzt ist es aus der Datenbank verschwunden. Aber ...

Ihre vorherige Sitzung weiß nichts darüber, und wenn Sie ein .get() verwenden, gibt es zurück, was in seiner Identitätskarte ist: ein Python-Objekt mit einer ID. Es wird nicht erneut geladen, es sei denn, Sie schließen die Sitzung oder erzwingen eine Aktualisierung von der Datenbank (ich kann mich nicht erinnern, wie OTOH dies tut, aber Sie können es irgendwo in der Dokumentation). Wenn Sie eine saubere dritte Sitzung verwendet haben, hätte sie eine neue Identitätskarte und würde keinen Verweis auf das Python-Objekt beibehalten, so dass Sie erhalten, was Sie erwarten, d. kein Ergebnis. Dies ist beabsichtigt, weil die Identity Map es SQLAlchemy ermöglicht, eine Reihe von Änderungen zu einer optimalen SQL-Abfrage zusammenzufassen, die nur beim Commit ausgelöst wird.

Also yeah, Sie sehen den Abruf von der Identity Map, die noch am Leben ist. (Sie können es sogar im interaktiven Interpreter öffnen und herumstochern). Und es macht Sinn, weil Sie sagen, dass Sie zwei Threads mit verschiedenen Webanforderungen haben und dass Sie einen längeren Weg mit einem Objekt machen, wenn eine andere Anfrage das Objekt löscht. Der erste Thread sollte den Python-Code, der mit dem Objekt arbeitet, nicht blockieren, da dies zufällige Ausnahmen auslösen würde, unabhängig davon, wo Sie sich im Code befanden. Es sollte nur denken, dass es seine Sache tun kann, und dann auf commit fehlschlagen, einen Rollback auszulösen.

HTH

+0

Ich habe also zwei Sitzungen. Einer im Test und einer in der App, als ich mit der Anfrage darauf traf. Also müsste ich die Sitzung entfernen und eine neue erstellen, um dies zu testen? – brandonbanks

+0

Das war hilfreich, um auf diese Weise nachzudenken. Ich konnte nicht aus der Datenbank aktualisieren. Ich versuchte 'db.session.refresh (db.session.query (MyObject) .get (mein_object.id))', aber das würde 'None' zurückgeben, selbst wenn das Objekt in der Datenbank existierte. – brandonbanks

0

db.session.expunge_all()

"Entfernen Sie alle Objektinstanzen aus dieser Session ..."

http://docs.sqlalchemy.org/en/latest/orm/session_api.html#sqlalchemy.orm.session.Session.expunge_all

Oder einfach Trigger nach jeder Anfrage db.session .remove()

Zum Beispiel in Flask mit SQLAlchemy scoped_session:

@app.teardown_appcontext 
def shutdown_session(exception=None): 
    db.session.remove() 

"Die scoped_session.remove() -Methode entfernt wie immer die aktuelle Sitzung, die dem Thread zugeordnet ist, falls vorhanden. Ein Vorteil des threading.local() -Objekts besteht jedoch darin, dass, wenn der Anwendungs-Thread selbst endet, der "Speicher" für diesen Thread ebenfalls "Garbage Collected" ist.Daher ist es in der Tat "sicher", den lokalen Thread des Threads mit einer Anwendung zu verwenden, die Threads erzeugt und abreißt, ohne scoped_session.remove() aufrufen zu müssen. Der Umfang der Transaktionen selbst, dh sie über Session.commit() oder Session.rollback() zu beenden, wird jedoch in der Regel immer noch etwas sein, zu dem zu gegebener Zeit explizit arrangiert werden muss, es sei denn, die Anwendung verknüpft tatsächlich die Lebensdauer eines Threads auf die Lebensdauer einer Transaktion.“

http://docs.sqlalchemy.org/en/latest/orm/contextual.html#thread-local-scope http://docs.sqlalchemy.org/en/latest/orm/contextual.html#using-thread-local-scope-with-web-applications

+0

Ich habe versucht 'db.session.expunge_all()' und ich bekomme einen 'DetachedInstanceError'. Das Objekt ist nicht an eine Sitzung gebunden. – brandonbanks

1

nach einem fairen Betrag von Lesen und Testen wir eine neue Sitzung zu schaffen fanden die einfachste Lösung zu sein. ich war, um herauszufinden, nicht in der Lage, wie auffrischen der Datensatz aus der Datenbank, obwohl der Datensatz veraltet war

Hier ist ein paar Lesung, die ich tat:

when to commit and close a session

is the session a cache

is the session thread safe

understanding sqlalchemy session

Hier ist, wie ich das Problem gelöst, indem eine neue DB-Verbindung zu schaffen und Sitzung mit Flask-SQLAlchemy:

[my other imports...] 
from flask.ext.sqlalchemy import SQLAlchemy 

[my other code...] 

def test_it_should_delete_my_object(self): 
    my_object = MyObject() 
    db.session.add(my_object) 
    db.session.commit() 
    response = requests.delete('{}/my-object/{}'.format(
     os.environ.get('MY_URL'), 
     my_object.id 
    )) 
    self.assertTrue(response.json()['success']) 
    self.assertEqual(200, response.status_code) 

    new_db = SQLAlchemy(app) // establishing a new connection 
    result = new_db.session.query(MyObject).get(my_object.id) // by using a new 
    self.assertIsNone(result) 

Vielen Dank für die Hilfe. Ich werde mehr darüber recherchieren.

+1

Ja, das sollten Sie tun, Sitzungen schließen und neue erstellen. Das Erstellen von Sitzungen ist in SQLAlchemy günstig. Für was es wert ist, empfehle ich nicht Flask-SQLAlchemy, ich persönlich bevorzuge einfach nur sqlalchemy. Flask-SQLAlchemy fügt nicht viel Bequemlichkeit auf Kosten von Magie hinzu. Sie sehen eine Menge Fragen von Leuten, die Flask-SQLAlchemy verwenden, die nicht wissen, was das Sitzungsmanagement macht. –

+0

Danke für die Eingabe. Ich werde nur SQLAlchemy verwenden. – brandonbanks

+0

+1 für einfache SQLAlchemy ohne Flask-SQLAlchemy. Sie können Sitzungen und Modelle außerhalb der App verwenden. Zum Beispiel in einigen Hintergrundaufgaben von DB-Wartungsmodulen. Mit Flask-SQLAlchemy benötigen Sie eine App-Instanz mit app_context, um Modelle zu verwenden. –

Verwandte Themen