[patch] [test] Querying detached instances

Issue #1295 resolved
Former user created an issue

Now there are two ways to load instances without puting them into session which was used for query - use another session or write code like this:

def get_detached_instances(mapper):
    session_objects = list(session)
    session.expunge_all()

    ret = session.query(mapper).all()

    session.expunge_all()
    session.add_all(session_objects)

    return ret

First way make programmer create session objects in such place that should not aware about it. Second way is just ugly and inefficient.

Proposed patch allows to call query's method detached before actually loading instances: session.query(mapper).detached().all()


Angri

Comments (9)

  1. Mike Bayer repo owner

    I'm -1 on this method as it's purpose is not clear, and any set of objects can be loaded without associating them with the current session by just using a new session. That would be the "one way to do it" here. the second method you've outlined seems very tedious. the method proposal would be a third. All for a use case that I don't see the purpose of in the first place.

  2. Former user Account Deleted

    I'll describe my use case. I have a table called "revision" which holds in pickle-field snapshots of objects from another table (it is some kind of vcs, but much more simple). Every snapshot includes atomic fields values and lists of related objects primary-keys. For example, object "user" may have first snapshot like {'name': 'john doe', 'phones': 5}, and second like {'name': 'join doe', 'phones': 4} - this means that one record from "user.phones" relation property was removed between this two revisions. And when I need to ressurect object from its first revision, I should create empty detached instance "phoenix = User()", than to assign atomic fields from snapshot to this new instance, than to populate its relation property 'phones' with actual objects with pks in 5. Obviously, if I write

    phoenix.phones = session.query(Phone).filter(Phone.id.in_([5](4,))).all()
    

    "phoenix" will be attached to session "session", even if I don't need it. And I can not just call "expunge" for every "Phone" object, because it may persist in session already, and this action can break outer code somehow. And I'm really not sure that creating sessions is Revision.resurrect()'s case (project is web-app and I using scoped_session).

  3. Mike Bayer repo owner

    Replying to guest:

    should create empty detached instance "phoenix = User()",

    that's not detached, that's transient - adding it to a session would result in an INSERT.

    than to assign atomic fields from snapshot to this new instance, than to populate its relation property 'phones' with actual objects with pks in 5.

    i'm not really sure what the detached part of this is, if you are resurrecting state from an outside source wouldn't you be creating that state, then putting it in to the session using merge() ? if you needed to associate state from a Query, there's no need for detachment there.

    Obviously, if I write {{{ phoenix.phones = session.query(Phone).filter(Phone.id.in_(5)).all() }}} "phoenix" will be attached to session "session", even if I don't need it. And I can not just call "expunge" for every "Phone" object, because it may persist in session already, and this action can break outer code somehow. And I'm really not sure that creating sessions is Revision.resurrect()'s case (project is web-app and I using scoped_session).

    You just need a temporary session which you can get using create_session(). the autoflush/autocommit etc. doesn't really matter since it starts as empty and is discarded immediately. I still have not put a using_session() method on Query for some reason but if i get around to it you could even use an existing query, and say query.using_session(create_session()).all().

  4. Former user Account Deleted

    should create empty detached instance "phoenix = User()",

    that's not detached, that's transient - adding it to a session would result in an INSERT.

    Oh, yes. Btw in my usage scenario saving "phoenix" back doesn't need at all.

    than to assign atomic fields from snapshot to this new instance, than to populate its relation property 'phones' with actual objects with pks in 5.

    i'm not really sure what the detached part of this is, if you are resurrecting state from an outside source wouldn't you be creating that state, then putting it in to the session using merge() ?

    No, assume that I just only want to show "what it was x days ago" using the same code as used for showing actual object's state.

    You just need a temporary session which you can get using create_session()

    Ok, I understood. Anyway, it's strange for me to have to create temporary session (in despite of fact that the whole project is satisfied with one scoped session), populate it making queries, than clear and drop it, if all I need is detached instances.

  5. Mike Bayer repo owner

    Replying to guest:

    Ok, I understood. Anyway, it's strange for me to have to create temporary session (in despite of fact that the whole project is satisfied with one scoped session), populate it making queries, than clear and drop it, if all I need is detached instances.

    well detaching instances that are immediately loaded from Query seems strange too. There may be half the objects already loaded, and some may even have pending changes on them. but we detach them based on criterion , do we pre-flush those changes ? it just seems like a strange method.

    you can make your own subclass of query which includes detach()....I just don't understand what semantics the method would have as a core method, and I'm trying not to add methods to Query unless we really really understand their behavior in full.

  6. Former user Account Deleted

    well detaching instances that are immediately loaded from Query seems strange too. So one can think about it as not "detach loaded objects" but as "do not attach loaded objects" :)

    There may be half the objects already loaded, and some may even have pending changes on them. Exactly! And this is what I want: to be able to load object(s) (without creating session where it isn't needed) in its state as they are in the DB. And they should not interfere with already loaded objects, even if there are pending changes or even if they all are deleted.

    you can make your own subclass of query which includes detach() I wish it was so easy :( If you take a look on the patch above you can see, that it brings almoust nothing to query and it is impossible to achive my needs externally without monkey-patching session.identity_map

  7. Mike Bayer repo owner

    oh i didnt see the mapper part. yeah sorry, I think creating a fresh session is the obvious way to accomplish this use case.

  8. Log in to comment