summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/ext/associationproxy.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2006-11-03 01:17:28 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2006-11-03 01:17:28 +0000
commit695f65db853a7b74a1ce2da75d8e3c55bbafae81 (patch)
treedd66a8f126fa1c44cfb7349c319168ab6c3b0a0f /lib/sqlalchemy/ext/associationproxy.py
parent14845494113b5327b2ed7f8ed13aed9bc9ce27b2 (diff)
downloadsqlalchemy-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.py96
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
+