"De-instrument" an instantiated object from the sqlalchemy ORM
Is there an easy way to "de-instrument" an instantiated class coming from sqlalchemy's ORM, i.e., turn it into a regular object?
I.e., suppose I have a Worker class that's mapped to a worker table:
class Worker(object):
def earnings(self):
return self.wage*self.hours
mapper(Worker,workers)
where workers is a reflected table containing lots of observations. The reason I want to do this is that methods like worker.earn开发者_如何学编程ings() are very slow, on account of all the sqlalchemy overhead (which I don't need for my application). E.g., accessing self.wage is about 10 times slower than it would be if self.wage was a property of a regular class.
If you need to permanently deinstrument a class, just dispose of the mapper:
sqlalchemy.orm.class_mapper(Worker).dispose()
SQLAlchemy instrumentation lives as property descriptors on the class object. So if you need separate deinstrumented versions of objects you'll need to create a version of the class that doesn't have the descriptors in it's type hierarchy.
A good way would be to have a persistent subclass for each model class and create the mappers to the persistent classes. Here's a class decorator that creates the subclass for you and adds it as a class attribute on the original:
def deinstrumentable(cls):
"""Create a deinstrumentable subclass of the class."""
def deinstrument(self):
"""Create a non-instrumented copy of the object."""
obj = cls.__new__(cls)
obj.__dict__.update(self.__dict__)
del obj._sa_instance_state
return obj
persistent = type('Persisted%s' % cls.__name__, (cls,), {
'Base': cls,
'deinstrument': deinstrument
})
return persistent
You would use it in the definition like this:
@deinstrumentable
class Worker(object):
def earnings(self):
return self.wage*self.hours
mapper(Worker, workers)
And when you have a persistent object, you can create a deinstrumented version of it like this:
worker = session.query(Worker).first()
detached_worker = worker.deinstrument()
You can create a deinstrumented version directly like this:
detached_worker = Worker.Base()
If you know the names of the fields you want, say you have them in a list of strings called fields
, and those of the methods you want, like earnings
in your example, in a list of strings called methods
, then:
def deinstrument(obj, fields, methods):
cls = type(obj)
class newcls(object): pass
newobj = newcls()
for f in fields:
setattr(newobj, f, getattr(obj, f))
for m in methods:
setattr(newcls, m, getattr(cls, m).im_func)
return newobj
You'll probably want __name__
among the fields
strings, so that the new object's class has the same name as the one you're "de-instrumenting".
精彩评论