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.

Reply via email to