There's a lot of detail here but the antipattern I see is you're creating a 
session just to do a getById() then closing it.    A single Session should 
frame a logical series of operations.  When those operations are complete, you 
commit your session if needed, close it out, then throw everything away.   
Using detached objects is only when moving them in and out of a cache or 
passing them across thread or process boundaries to another waiting worker that 
will immediately re-merge them back into a different Session, which is itself 
framing out a logical series of operations.


On Mar 23, 2011, at 2:48 PM, Hector Blanco wrote:

> Hello everyone...
> 
> I am getting detached instances error and I don't really know why.
> There's something I don't get with the session, but I have been
> reading the documentation thoroughly, trying many things and I can't
> get rid of all the DetachedInstance exceptions... That's why I think
> I'm doing something [deeply] wrong ( I think I have a core "concept
> error")
> 
> I have designed my application in the following way:
> 
> The idea is to have my mapped classes (classes serialized in a MySQL
> table) and for each class  I would have a "manager" that contains a
> bunch of methods that deal with the database.
> 
> Let's say I have:
> 
> class Foo(Database.Base):
>       _id = Column("id", Integer, primary_key=True, key="id")
>       _name = Column("name", String(50))
> 
> Then I will also have a "FooManager" like this:
> 
> ----------------------------------------
> from myLibraries.foos import Foo
> 
> class FooManager(object):
> 
>       @classmethod
>       def getById(cls, idParam, relationshipsToPreLoad=None):
>               retval = None
>               if relationshipsToPreLoad is None:
>                       relationshipsToPreLoad = 
> DatabaseUtils.getRelationships(Foo.Foo)
>               session = Database.Session()
>               try:
>                       if (relationshipsToPreLoad):
>                               retval = session.query(Foo.Foo).options(*
> [joinedload_all(relationshipToPreLoad)
>                                               for relationshipToPreLoad in
> relationshipsToPreLoad]).get(int(idParam))
>                       else:
>                               retval = 
> session.query(Foo.Foo).get(int(idParam))
>                       session.commit()
>               except exc.ResourceClosedError, err:
>                       log.debug('::getById > Manager %s > Got exception "%s".\
>                                       Probably the request was canceled by 
> the user' % (cls.__name__, err))
>               finally:
>                       session.close()
>                       pass
>               return retval
> 
>       @classmethod
>       def update(cls, element):
>               if isinstance(element, Foo.Foo):
>                       session = Database.Session()                            
>                       try:
>                               element = session.merge(element)
>                               session.commit()
>                       finally:
>                               session.close()
>                               log.debug("::update> Updated %s with id==%s" %
> (element.__class__.__name__, element.id))
>                               return element
>               else:
>                       raise TypeError("Received parameter %s of type %s when 
> expecting
> %s" % (element, type(element), Foo.Foo.__classname__))
>                       return None
> ----------------------------------------
> 
> So when, in another part of the application I want to load the element
> Foo.Foo with id 6, I could just do:
> 
> myFooInstance = myLibraries.foos.FooManager.FooManager.getById(6)
> print myFooInstance.id
> print myFooInstance.name
> 
> Or, if I create a new Foo instance, I can do:
> newFoo = Foo.Foo()
> newFoo.name = "fooname"
> myLibraries.foos.FooManager.FooManager.update(newFoo)
> 
> and the "newFoo" will be added to the database.
> 
> The "Database" module is just a few lines long and contains the session maker:
> 
> ---- Database.py -----
> DSN = "mysql://mysqluser:*******@localhost/ev?charset=utf8"
> engine = create_engine(DSN)
> Session = scoped_session(sessionmaker(bind=engine))
> 
> Base = declarative_base()
> -----------------------------------------------
> 
> 
> The whole idea is separating the access to the database from the
> classes itself (the manager's idea is something like "go to the
> database, do your thing, give me a regular python class", but it's not
> working fine. I've been able to find some workarounds but it's still
> not working fine. Do you think this "Class/ ClassManager" type of
> implementation a good idea? As you can see, in every method of the
> manager I create a new instance of the session, do whatever and close
> it. Is that the way the session is supposed to be used? Should the
> session be global? (I can create an instance in Database.py)
> 
> I saw this:
> http://groups.google.com/group/sqlalchemy/browse_thread/thread/f53f98cbbaee7b2b/14c3d8c7229ba0bc?lnk=gst&q=DetachedInstanceError#14c3d8c7229ba0bc
> 
> which seems to detail a similar problem. I tried both methods detailed
> there, but I'm still getting detached instances errors. And it seems
> to me that it recommends not to use detached instances (which, as far
> as I understand, is what I'm trying to do here)
> 
> Any hint will be deeply appreciated. Thank you everyone!
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to 
> [email protected].
> 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 [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to