My application allows users to create and delete Site
objects. I have implemented this using session.add()
and session.delete()
. I then have 'Save' and 'Reset' buttons that call session.commit()
and session.rollback()
.
If I add a new Site
, then save/commit it, and then delete it, everything goes OK. However, if I try to remove an object from the session before it's been saved, I get a 'not persisted' error.
Code:
self.newSite = Site('foo') self.session.add(self.newSite) print self.session.new self.session.delete(self.newSite)
Output:
IdentitySet([<Site('foo')>]) Traceback (most recent call last): File "C:\Program Files\Eclipse\dropins\plugins\org.python.pydev.debug_2.2.1.2011071313\pysrc\pydevd_comm.py", line 744, in doIt result = pydevd_vars.evaluateExpression(self.thread_id, self.frame_id, self.expression, self.doExec) File "C:\Program Files\Eclipse\dropins\plugins\org.python.pydev.debug_2.2.1.2011071313\pysrc\pydevd_vars.py", line 375, in evaluateExpression result = eval(compiled, updated_globals, frame.f_locals) File "<string>", line 1, in <module> File "C:\Python27\Lib\site-packages\sqlalchemy\orm\session.py", line 1245, in delete mapperutil.state_str(state)) InvalidRequestError: Instance '<Site at 0x1ed5fb0>' is not persisted
I understand what's happening here, but I'm not sure what I should be doing instead.
Is there some other method of removing a not-yet-persisted object from a session? Or should I be calling session.flush()
before attempting a deletion, in case the object I want to delete hasn't been flushed yet?
If it's the latter, how come session.query()
auto-flushes (ensuring that pending objects show up in the query results), but session.delete()
doesn't (which would ensure that pending objects can be deleted without error).
delete() is invoked upon an object and the Session is flushed, the row is deleted from the database.
flush() communicates a series of operations to the database (insert, update, delete). The database maintains them as pending operations in a transaction.
first() applies a limit of one within the generated SQL, so that only one primary entity row is generated on the server side (note this may consist of multiple result rows if join-loaded collections are present). Calling Query. first() results in an execution of the underlying query.
You can use session. refresh() to immediately get an up-to-date version of the object, even if the session already queried the object earlier.
You can Session.expunge()
it. I think the rationale with delete()
being that way is, it worries you're not keeping track of things if you send it a pending. But I can see the other side of the story on that, I'll think about it. Basically the state implied by delete()
includes some assumptions of persistence but they're probably not as significant as I'm thinking. An "expunge or delete" method then comes to mind, which is funny that's basically the "save or update" we originally copied from Hibernate, which just became "add". "add" can do the transitions of transient->pending as well as detached->persistent - would a potential "remove()" do both pending->transient and persistent->deleted ? too bad the scoped session already has "remove()"....
Session.query()
autoflushes because it's about to go out to the database to emit some SQL to get some rows; so whatever you have locally needs to go out first. delete()
just marks the state of an object so there's no need to invoke any SQL. If we wanted delete()
to work on a pending, we'd just change that assertion.
Interestingly, if you rollback()
the session, whatever you've add()
'ed within that session, whether or not it got flushed, is expunged.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With