so I think the safer options here would be: 1. for obj in session.new, use session.expunge(obj) 2. for obj in session.deleted, use session.add(obj)
so #1, the difference is that it removes from _new and also detaches the state from the session, which just means it removes the "session_id". you can re-attach API-wise using session.add(), once the object has an identity key assigned, which means instance_state(obj).key = sqlalchemy.orm.util.identity_key(class, id) . for #2, that should amount to exactly a remove from session._deleted. On Apr 16, 2012, at 10:30 AM, rdunklau wrote: > > > On Apr 16, 4:16 pm, Michael Bayer <mike...@zzzcomputing.com> wrote: >> OK the one area that I'd check on is how exactly you remove items from >> new/deleted. The public API for that is pretty much to expunge() the >> object from the Session (I think expiring an object should remove from >> deleted also but I'd have to check). The use case of modifying those >> lists directly is not covered by tests, meaning I can't guarantee removing >> from .new or .deleted is the equivalent to that action not happening in the >> first place. > > Here is the complete before_flush method: > > def before_flush(self, session, flush_context, states): > for state in session.dirty: > if self._is_managed(state): > self.dirty.append(state) > state.modified = False > for state in session.deleted: > if self._is_managed(state): > self.deleted.append(state) > state.deleted = False > session._deleted.pop(state._sa_instance_state) > for state in session.new: > if self._is_managed(state): > state.new = False > self.new.append(state) > session._new.pop(state._sa_instance_state) > >> >> On Apr 16, 2012, at 9:33 AM, rdunklau wrote: >> >> >> >> >> >> >> >>> I followed your suggestions, and implemented it in the following way: >> >>> - a "SessionListener" class, responsible for maintaining the >>> "alternate" state of the sesssion is instantiated. The class defines a >>> before_flush, and after_flush methods, which are registered as event >>> listeners for their respecting events >> >>> - the before flush method scans the new, dirty and deleted states from >>> the session, removing them from the new and deleted list as need, and >>> clearing the modified flag if needed from states which it manages >> >>> - the after flush method performs the actual save/delete/update >>> behavior. >> >>> I did not bother "restoring" the new and deleted states from the >>> session. It may cause problem in the future, or it may not, since a >>> pattern in this particular application is to redirect to a new page >>> after every commit, thus minimizing the risk to access invalid states. >>> I don't know what will happen to dependent states though... I don't >>> think we implicitly add or remove states from the session by adding / >>> removing them to / from a relationship concerning those mappers, >>> but... >> >>> So, while it seems to work, I surely don't know enough about the >>> possible ramifications to be 100% confident in my code... >> >>> On Apr 13, 5:33 pm, rdunklau <rdunk...@gmail.com> wrote: >>>> On Apr 13, 3:12 pm, Michael Bayer <mike...@zzzcomputing.com> wrote: >> >>>>> On Apr 13, 2012, at 6:08 AM, Ronan Dunklau wrote: >> >>>>>> Hello. >> >>>>>> Prior to sqlalchemy 0.7.6, we implemented our own mappers to override >>>>>> the _save_obj and _delete_obj methods. >> >>>>> that's very interesting. There is an old ticket to make this kind of >>>>> thing an API, which would work similarly, that is you make your own >>>>> mapper (note this is 3 years old at this point, may have some out of date >>>>> aspects) : http://www.sqlalchemy.org/trac/ticket/1518. I imagine you >>>>> had to reimplement a lot of private things to get that to work, dealing >>>>> with InstanceState, etc. >> >>>> In fact, not at all. We only had to reimplement the _save_obj and >>>> _delete_obj methods on the mapper to perform basic sanity checks, and >>>> listen on the session commit (using Flask-SQLAlchemy signalling >>>> session). >> >>>>> If you guys had any thoughts on 1518 and/or APIs to contribute, it could >>>>> conceivably go into 0.8. I'm hoping to have time to really churn out >>>>> 0.8 in the early summer, a lot of it is ready it just needs to be >>>>> assembled. >> >>>> I don't really know, since we only have this one application using >>>> this for now. >>>> Our need would be to signal any potential error at flush time, and >>>> then perform the actual save operation. >> >>>>>> Those methods don't exist anymore, and instead this responsibility is >>>>>> transfered to the persistence module. >> >>>>> yeah what I was actually going for there was to make those really long >>>>> _save_obj() and _delete_obj() methods a lot easier to understand. I saw >>>>> a talk by Wes McKinney and he stressed his habit of "make no method more >>>>> than 50 lines". It nagged at me as it seemed to break those methods up >>>>> like that would make them less efficient. But actually not and it >>>>> worked out really well. (except for what you were doing of course !) >> >>>>>> A little bit of context: >> >>>>>> We are using postgresql foreign tables, which are read only. So, the >>>>>> purpose of these custom mappers was to prevent insert/update/delete >>>>>> statements from going to the database, and instead save the mapped >>>>>> entities in their respective storages. >> >>>>> so what in this case were the "respective storages", was it a different >>>>> set of tables in a different database ? >> >>>> We are mainly writing files to disk, be it ical files, images or >>>> whatever else. >> >>>>>> Is there a way to achieve the same goal with the new architecture ? >>>>>> By the way, is there a documentation for the internals of the new >>>>>> architecture ? >> >>>>> in fact the internals of the new system work exactly the same as the old >>>>> system, they are just organized into more discrete methods. >> >>>>> the details of your system of storage would make a big difference here. >>>>> There's two thoughts I have immediately. One is, just for the time >>>>> being you can monkeypatch persistence.save_obj and >>>>> persistence.delete_obj, such that they first check the mappers to see if >>>>> they are in fact your special overridden Mapper objects, and use your own >>>>> _save_obj and _delete_obj in that case, else continue on to the default >>>>> implementation of these functions. The behavioral contract of these >>>>> functions hasn't changed. >> >>>>> The other is, if these objects really persist in some other way, like >>>>> you're posting them to a web service or something, you could deal with >>>>> that using session events. In before_flush() you'd pluck them out of the >>>>> flush plan by doing what you need with them, then resetting their history >>>>> so that they are no longer seen as "dirty", and they wouldn't be included >>>>> in save_obj at all. If you're dealing with .new and .deleted also >>>>> there'd be slightly different steps to handle those. You might even >>>>> implement a before_flush()/after_flush() pair that totally removes these >>>>> objects within the flush process and puts them back afterwards in a >>>>> clean, persistent state. I haven't tried this kind of scheme before >>>>> but it at least would be public APIs. >> >>>> That's a really nice idea, I will try it next week. >> >>>>> A third is we actually add the altpersist API. I've been holding off on >>>>> it as I typically wait for some real use cases to figure out how these >>>>> things should work. >> >>>> Thank you for your help >> >>>> Regards, >> >>>> -- >>>> Ronan Dunklau >> >>> -- >>> 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 >>> athttp://groups.google.com/group/sqlalchemy?hl=en. > > -- > 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. > -- 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.