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.