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.

Reply via email to