Andreas
Something like this is available on a roll-your-own basis via Python properties along with some mapper tricks:
http://www.sqlalchemy.org/docs/04/mappers.html#advdatamapping_mapper_overriding
I would be +1 for such a feature implemented on mapped instances, could be useful for detecting those hard-to-find bugs, but I can't think of a nice and simple API for it. For mapped instances via Query(), it could be an .option(), but I can't see a good way for its use on relation()s. Also not sure if such a feature would throw an exception on attribute setting, or whether it ought to simply be ingored during a flush (OIOW, have it's "dirty" flag locked down to False)
another, similar or not, feature i needed while doing dbcook, was a
readonly/loadonly mapper; i.e. a mapper for a sort-of intermediate
base-class which should not have its own instances; only subclasses may have
instances/DB-footprint. That i made via MapperExt, throwing at
before_insert/update/delete.
--On 21. Dezember 2007 16:33:34 -0500 Michael Bayer
<mik...@zzzcomputing.com> wrote:
With all respect, this is not a useful answer. Even with tests (unittests
and weeks of manual tests) I had the case that a simple programming error
(of my own) produced a data disaster after some weeks. There is no 100%
test coverage. Tests don't solve all problems. There is sometimes the need
for a better security belt.
Andreas
I am certainly suggesting a fixture that detects illegal assignments
to attributes. That it be limited to just unit tests is only a
suggestion. To establish this functionality regardless of
environment, like Rick said just create properties which prohibit
assignment. Create mappers like this:
class AttrGetter(object):
def __init__(self, name):
self.name = name
def __get__(self, instance, name):
if instance is None:
return self
return getattr(instance, '_' + name)
def __set__(self, instance, value):
raise AssertionError("Sets are not allowed")
def __delete__(self, instance):
raise AssertionError("Deletes are not allowed")
class MyClass(object):
somecolumn = AttrGetter('somecolumn')
someothercolumn = AttrGetter('someothercolumn')
mapper(MyClass, sometable, properties={
'_somecolumn':sometable.c.somecolumn,
'_someothercolumn':sometable.c.someothercolumn
})
To automate the above process with no modifications to source code,
create an instrumented mapper() function which applies the above
recipe to all table columns:
from sqlalchemy.orm import mapper as _mapper
def mapper(cls, table, **kwargs):
attrs = {}
for c in table.c:
attrs['_' + c.key] = c
setattr(cls, c.key, AttrGetter(c.key))
properties = kwargs.setdefault('properties', {})
properties.update(attrs)
return _mapper(cls, table, **kwargs)
Hope this helps.