Re: [sqlalchemy] changes flushed for expunged relationships
On Oct 17, 2012, at 9:55 PM, Kent wrote: The attached script fails with sqlalchemy.exc.InvalidRequestError: Instance 'Bug at 0x1e6f3d10' has been deleted. Use the make_transient() function to send this object back to the transient state. While this example is somewhat convoluted, I have a few questions about sqlalchemy behavior here: 1) At the session.flush(), even though the Rock and the bugs relationship have been expunged, the pending delete still is issued to the database. Would you expect/intend sqlalchemy to delete even after the expunge()? no, because the Rock you have merged has established that the Bug is no longer associated with it. You expunge the Rock, there's no Bug attached to it to be expunged. 2) After the flush(), shouldn't the history of the 'bugs' relationship have been updated to reflect the statement issued to the database? (See print statement) yes, because merged is not in that Session anymore.The flush() is what resets the history. Clearly it's not going to go out to find objects that aren't in that Session. 3) The InvalidRequestError is only raised if the 'bugs' relationship has a backref, otherwise it isn't raised. Any idea why? removing the Bug.rock path means that when you expunge merged, there is nothing left in the flush process to handle the orphan cascade you're looking for here. The DELETE does not occur so the Bug that's present on merged can go right back in. 4) Don't hate me for asking: is there a work around? I'm trying to understand this scenario since in a rare case, it presents. The workaround is don't use expunge().This is not a method I ever use for anything, actually. And especially, if you manipulate the state within the Session, then expunge() random segments of that state, you can hardly expect flush() to have a clear idea of what you intend. -- You received this message because you are subscribed to the Google Groups sqlalchemy group. To post to this group, send email to sqlalchemy@googlegroups.com. To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
Re: [sqlalchemy] changes flushed for expunged relationships
Thank you for the clarifications. On 10/18/2012 11:43 AM, Michael Bayer wrote: On Oct 17, 2012, at 9:55 PM, Kent wrote: The attached script fails with sqlalchemy.exc.InvalidRequestError: Instance 'Bug at 0x1e6f3d10' has been deleted. Use the make_transient() function to send this object back to the transient state. While this example is somewhat convoluted, I have a few questions about sqlalchemy behavior here: 1) At the session.flush(), even though the Rock and the bugs relationship have been expunged, the pending delete still is issued to the database. Would you expect/intend sqlalchemy to delete even after the expunge()? no, because the Rock you have merged has established that the Bug is no longer associated with it. You expunge the Rock, there's no Bug attached to it to be expunged. 2) After the flush(), shouldn't the history of the 'bugs' relationship have been updated to reflect the statement issued to the database? (See print statement) yes, because merged is not in that Session anymore.The flush() is what resets the history. Clearly it's not going to go out to find objects that aren't in that Session. 3) The InvalidRequestError is only raised if the 'bugs' relationship has a backref, otherwise it isn't raised. Any idea why? removing the Bug.rock path means that when you expunge merged, there is nothing left in the flush process to handle the orphan cascade you're looking for here. The DELETE does not occur so the Bug that's present on merged can go right back in. 4) Don't hate me for asking: is there a work around? I'm trying to understand this scenario since in a rare case, it presents. The workaround is don't use expunge().This is not a method I ever use for anything, actually. And especially, if you manipulate the state within the Session, then expunge() random segments of that state, you can hardly expect flush() to have a clear idea of what you intend. -- You received this message because you are subscribed to the Google Groups sqlalchemy group. To post to this group, send email to sqlalchemy@googlegroups.com. To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
Re: [sqlalchemy] changes flushed for expunged relationships
On Thursday, October 18, 2012 11:43:50 AM UTC-4, Michael Bayer wrote: On Oct 17, 2012, at 9:55 PM, Kent wrote: The attached script fails with sqlalchemy.exc.InvalidRequestError: Instance 'Bug at 0x1e6f3d10' has been deleted. Use the make_transient() function to send this object back to the transient state. While this example is somewhat convoluted, I have a few questions about sqlalchemy behavior here: 1) At the session.flush(), even though the Rock and the bugs relationship have been expunged, the pending delete still is issued to the database. Would you expect/intend sqlalchemy to delete even after the expunge()? no, because the Rock you have merged has established that the Bug is no longer associated with it. You expunge the Rock, there's no Bug attached to it to be expunged. I am still slightly unclear on this: since sqlalchemy *does *delete the Bug, I assume it is marked for deletion in some way when the 'bugs' relationship is merged to the empty list []. Is that accurate? Please help me understand why it does not show up in session.deleted, which is IdentitySet([])? -- You received this message because you are subscribed to the Google Groups sqlalchemy group. To view this discussion on the web visit https://groups.google.com/d/msg/sqlalchemy/-/pnnq3lOodtYJ. To post to this group, send email to sqlalchemy@googlegroups.com. To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
Re: [sqlalchemy] changes flushed for expunged relationships
On Oct 18, 2012, at 2:46 PM, Kent wrote: On Thursday, October 18, 2012 11:43:50 AM UTC-4, Michael Bayer wrote: On Oct 17, 2012, at 9:55 PM, Kent wrote: The attached script fails with sqlalchemy.exc.InvalidRequestError: Instance 'Bug at 0x1e6f3d10' has been deleted. Use the make_transient() function to send this object back to the transient state. While this example is somewhat convoluted, I have a few questions about sqlalchemy behavior here: 1) At the session.flush(), even though the Rock and the bugs relationship have been expunged, the pending delete still is issued to the database. Would you expect/intend sqlalchemy to delete even after the expunge()? no, because the Rock you have merged has established that the Bug is no longer associated with it. You expunge the Rock, there's no Bug attached to it to be expunged. I am still slightly unclear on this: since sqlalchemy does delete the Bug, I assume it is marked for deletion in some way when the 'bugs' relationship is merged to the empty list []. Is that accurate? Please help me understand why it does not show up in session.deleted, which is IdentitySet([])? This particular mark for deletion occurs on line 1817 of session.py, within flush(), as it iterates through the current list of dirty objects and tests the object to see if it's an orphan. For Bug, this holds true and it's marked for deletion. The mark for deletion due to orphan step always occurs within the flush process. It is ultimately based on a test for the parents of the target object, which in the course of operation can be set and unset multiple times, so it is easier to track just this one flag and then convert to a delete within the flush, rather than to delete/undelete continuously, especially considering that the delete operation itself needs to continue cascading. So to correct what I said about backrefs earlier, what is specifically happening here is, the backref is what enables Bug to be marked as dirty in the first place, that it's Bug.rock attribute has been modified. flush() then sees its an orphan and marks it as such. Without the backref, bug is in the Session, but is not marked as modified. Rock is gone so nothing else happens, the Session is clean. If Rock is not expunged, and the backref is not present on Bug, *then* the mark for deletion occurs in a different place, on line 473 of dependency.py where the presort_saves method of OneToManyDP (which is managing Rock.bugs) iterates through those records marked as removed from Rock.bugs, tests them for orphan, marks them as deleted. So this is a later path for mark for deletion, but is still based on the orphan flag status of the object. -- You received this message because you are subscribed to the Google Groups sqlalchemy group. To post to this group, send email to sqlalchemy@googlegroups.com. To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
[sqlalchemy] changes flushed for expunged relationships
The attached script fails with sqlalchemy.exc.InvalidRequestError: Instance 'Bug at 0x1e6f3d10' has been deleted. Use the make_transient() function to send this object back to the transient state. While this example is somewhat convoluted, I have a few questions about sqlalchemy behavior here: 1) At the session.flush(), even though the Rock and the bugs relationship have been expunged, the pending delete still is issued to the database. Would you expect/intend sqlalchemy to delete even after the expunge()? 2) After the flush(), shouldn't the history of the 'bugs' relationship have been updated to reflect the statement issued to the database? (See print statement) 3) The InvalidRequestError is only raised if the 'bugs' relationship has a backref, otherwise it isn't raised. Any idea why? 4) Don't hate me for asking: is there a work around? I'm trying to understand this scenario since in a rare case, it presents. Thanks very much! Kent -- You received this message because you are subscribed to the Google Groups sqlalchemy group. To view this discussion on the web visit https://groups.google.com/d/msg/sqlalchemy/-/6oYSFMbpnEsJ. To post to this group, send email to sqlalchemy@googlegroups.com. To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en. from sqlalchemy import * from sqlalchemy.orm import * from sqlalchemy.orm import attributes engine = create_engine('sqlite:///', echo=True) metadata = MetaData(engine) Session = sessionmaker(bind=engine) rocks_table = Table(rocks, metadata, Column(id, Integer, primary_key=True), ) bugs_table = Table(bugs, metadata, Column(id, Integer, primary_key=True), Column(rockid, Integer, ForeignKey('rocks.id')), ) class Object(object): def __init__(self, **attrs): self.__dict__.update(attrs) class Rock(Object): def __repr__(self): return 'Rock: id=[%s]' % self.__dict__.get('id') class Bug(Object): def __repr__(self): return 'Bug: id=[%s]' % self.__dict__.get('id') mapper(Rock, rocks_table, properties={'bugs': relationship(Bug, cascade='all,delete-orphan', backref=backref('rock',cascade='refresh-expire,expunge')) }) mapper(Bug, bugs_table) metadata.create_all() try: session = Session() r = Rock(id=1) r.bugs=[Bug(id=1)] session.add(r) session.commit() session = Session() r = Rock(id=1) r.bugs=[] merged = session.merge(r) session.expunge(merged) # if merged is now detached, should flush() still delete Bug? session.flush() # should history still have deleted Bug? print \n\nadd: %r\nunchanged: %r\ndelete: %r\n % attributes.get_history(merged, 'bugs') # this only fails if the backref 'rock' is present in relationship session.add(merged) finally: metadata.drop_all()