diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2006-11-03 01:17:28 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2006-11-03 01:17:28 +0000 |
| commit | 695f65db853a7b74a1ce2da75d8e3c55bbafae81 (patch) | |
| tree | dd66a8f126fa1c44cfb7349c319168ab6c3b0a0f /lib/sqlalchemy/ext/associationproxy.py | |
| parent | 14845494113b5327b2ed7f8ed13aed9bc9ce27b2 (diff) | |
| download | sqlalchemy-695f65db853a7b74a1ce2da75d8e3c55bbafae81.tar.gz | |
- added an assertion within the "cascade" step of ORM relationships to check
that the class of object attached to a parent object is appropriate
(i.e. if A.items stores B objects, raise an error if a C is appended to A.items)
- new extension sqlalchemy.ext.associationproxy, provides transparent "association object"
mappings. new example examples/association/proxied_association.py illustrates.
- some example cleanup
Diffstat (limited to 'lib/sqlalchemy/ext/associationproxy.py')
| -rw-r--r-- | lib/sqlalchemy/ext/associationproxy.py | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/lib/sqlalchemy/ext/associationproxy.py b/lib/sqlalchemy/ext/associationproxy.py new file mode 100644 index 000000000..644427902 --- /dev/null +++ b/lib/sqlalchemy/ext/associationproxy.py @@ -0,0 +1,96 @@ +"""contains the AssociationProxy class, a Python property object which +provides transparent proxied access to the endpoint of an association object. + +See the example examples/association/proxied_association.py. +""" + +from sqlalchemy.orm import class_mapper + +class AssociationProxy(object): + """a property object that automatically sets up AssociationLists on a parent object.""" + def __init__(self, targetcollection, attr, creator=None): + """create a new association property. + + targetcollection - the attribute name which stores the collection of Associations + + attr - name of the attribute on the Association in which to get/set target values + + creator - optional callable which is used to create a new association object. this + callable is given a single argument which is an instance of the "proxied" object. + if creator is not given, the association object is created using the class associated + with the targetcollection attribute, using its __init__() constructor and setting + the proxied attribute. + """ + self.targetcollection = targetcollection + self.attr = attr + self.creator = creator + def __init_deferred(self): + prop = class_mapper(self._owner_class).props[self.targetcollection] + self._cls = prop.mapper.class_ + self._uselist = prop.uselist + def _get_class(self): + try: + return self._cls + except AttributeError: + self.__init_deferred() + return self._cls + def _get_uselist(self): + try: + return self._uselist + except AttributeError: + self.__init_deferred() + return self._uselist + cls = property(_get_class) + uselist = property(_get_uselist) + def create(self, target): + if self.creator is not None: + return self.creator(target) + else: + assoc = self.cls() + setattr(assoc, self.attr, target) + return assoc + def __get__(self, obj, owner): + self._owner_class = owner + if obj is None: + return self + storage_key = '_AssociationProxy_%s' % self.targetcollection + if self.uselist: + try: + return getattr(obj, storage_key) + except AttributeError: + a = _AssociationList(self, obj) + setattr(obj, storage_key, a) + return a + else: + return getattr(getattr(obj, self.targetcollection), self.attr) + def __set__(self, obj, value): + if self.uselist: + setattr(obj, self.targetcollection, [self.create(x) for x in value]) + else: + setattr(obj, self.targetcollection, self.create(value)) + def __del__(self, obj): + delattr(obj, self.targetcollection) + +class _AssociationList(object): + """generic proxying list which proxies list operations to a different + list-holding attribute of the parent object, converting Association objects + to and from a target attribute on each Association object.""" + def __init__(self, proxy, parent): + """create a new AssociationList.""" + self.proxy = proxy + self.parent = parent + def append(self, item): + a = self.proxy.create(item) + getattr(self.parent, self.proxy.targetcollection).append(a) + def __iter__(self): + return iter([getattr(x, self.proxy.attr) for x in getattr(self.parent, self.proxy.targetcollection)]) + def __repr__(self): + return repr([getattr(x, self.proxy.attr) for x in getattr(self.parent, self.proxy.targetcollection)]) + def __len__(self): + return len(getattr(self.parent, self.proxy.targetcollection)) + def __getitem__(self, index): + return getattr(getattr(self.parent, self.proxy.targetcollection)[index], self.proxy.attr) + def __setitem__(self, index, value): + a = self.proxy.create(item) + getattr(self.parent, self.proxy.targetcollection)[index] = a + |
