return (orm.class_mapper(self.owning_class).
get_property(self.target_collection))
+ @property
def target_class(self):
"""The class the proxy is attached to."""
return self._get_property().mapper.class_
- target_class = property(target_class)
def _target_is_scalar(self):
return not self._get_property().uselist
- def _lazy_collection(self, weakobjref):
- target = self.target_collection
- del self
- def lazy_collection():
- obj = weakobjref()
- if obj is None:
- raise exceptions.InvalidRequestError(
- "stale association proxy, parent object has gone out of "
- "scope")
- return getattr(obj, target)
- return lazy_collection
-
def __get__(self, obj, class_):
if self.owning_class is None:
self.owning_class = class_ and class_ or type(obj)
return proxy
except AttributeError:
pass
- proxy = self._new(self._lazy_collection(weakref.ref(obj)))
+ proxy = self._new(_lazy_collection(obj, self.target_collection))
setattr(obj, self.key, (id(obj), proxy))
return proxy
-
+
def __set__(self, obj, values):
if self.owning_class is None:
self.owning_class = type(obj)
getter, setter = self.getset_factory(self.collection_class, self)
else:
getter, setter = self._default_getset(self.collection_class)
-
+
if self.collection_class is list:
- return _AssociationList(lazy_collection, creator, getter, setter)
+ return _AssociationList(lazy_collection, creator, getter, setter, self)
elif self.collection_class is dict:
- return _AssociationDict(lazy_collection, creator, getter, setter)
+ return _AssociationDict(lazy_collection, creator, getter, setter, self)
elif self.collection_class is set:
- return _AssociationSet(lazy_collection, creator, getter, setter)
+ return _AssociationSet(lazy_collection, creator, getter, setter, self)
else:
raise exceptions.ArgumentError(
'could not guess which interface to use for '
'proxy_factory and proxy_bulk_set manually' %
(self.collection_class.__name__, self.target_collection))
+ def _inflate(self, proxy):
+ creator = self.creator and self.creator or self.target_class
+
+ if self.getset_factory:
+ getter, setter = self.getset_factory(self.collection_class, self)
+ else:
+ getter, setter = self._default_getset(self.collection_class)
+
+ proxy.creator = creator
+ proxy.getter = getter
+ proxy.setter = setter
+
def _set(self, proxy, values):
if self.proxy_bulk_set:
self.proxy_bulk_set(proxy, values)
'no proxy_bulk_set supplied for custom '
'collection_class implementation')
+class _lazy_collection(object):
+ def __init__(self, obj, target):
+ self.ref = weakref.ref(obj)
+ self.target = target
-class _AssociationList(object):
- """Generic, converting, list-to-list proxy."""
-
- def __init__(self, lazy_collection, creator, getter, setter):
- """Constructs an _AssociationList.
+ def __call__(self):
+ obj = self.ref()
+ if obj is None:
+ raise exceptions.InvalidRequestError(
+ "stale association proxy, parent object has gone out of "
+ "scope")
+ return getattr(obj, self.target)
+
+ def __getstate__(self):
+ return {'obj':self.ref(), 'target':self.target}
+
+ def __setstate__(self, state):
+ self.ref = weakref.ref(state['obj'])
+ self.target = state['target']
+
+class _AssociationCollection(object):
+ def __init__(self, lazy_collection, creator, getter, setter, parent):
+ """Constructs an _AssociationCollection.
+
+ This will always be a subclass of either _AssociationList,
+ _AssociationSet, or _AssociationDict.
lazy_collection
A callable returning a list-based collection of entities (usually an
self.creator = creator
self.getter = getter
self.setter = setter
+ self.parent = parent
col = property(lambda self: self.lazy_collection())
+ def __len__(self):
+ return len(self.col)
+
+ def __nonzero__(self):
+ return bool(self.col)
+
+ def __getstate__(self):
+ return {'parent':self.parent, 'lazy_collection':self.lazy_collection}
+
+ def __setstate__(self, state):
+ self.parent = state['parent']
+ self.lazy_collection = state['lazy_collection']
+ self.parent._inflate(self)
+
+class _AssociationList(_AssociationCollection):
+ """Generic, converting, list-to-list proxy."""
+
def _create(self, value):
return self.creator(value)
def _set(self, object, value):
return self.setter(object, value)
- def __len__(self):
- return len(self.col)
-
- def __nonzero__(self):
- if self.col:
- return True
- else:
- return False
-
def __getitem__(self, index):
return self._get(self.col[index])
_NotProvided = util.symbol('_NotProvided')
-class _AssociationDict(object):
+class _AssociationDict(_AssociationCollection):
"""Generic, converting, dict-to-dict proxy."""
- def __init__(self, lazy_collection, creator, getter, setter):
- """Constructs an _AssociationDict.
-
- lazy_collection
- A callable returning a dict-based collection of entities (usually an
- object attribute managed by a SQLAlchemy relation())
-
- creator
- A function that creates new target entities. Given two parameters:
- key and value. The assertion is assumed::
-
- obj = creator(somekey, somevalue)
- assert getter(somekey) == somevalue
-
- getter
- A function. Given an associated object and a key, return the
- 'value'.
-
- setter
- A function. Given an associated object, a key and a value, store
- that value on the object.
-
- """
- self.lazy_collection = lazy_collection
- self.creator = creator
- self.getter = getter
- self.setter = setter
-
- col = property(lambda self: self.lazy_collection())
-
def _create(self, key, value):
return self.creator(key, value)
def _set(self, object, key, value):
return self.setter(object, key, value)
- def __len__(self):
- return len(self.col)
-
- def __nonzero__(self):
- if self.col:
- return True
- else:
- return False
-
def __getitem__(self, key):
return self._get(self.col[key])
del func_name, func
-class _AssociationSet(object):
+class _AssociationSet(_AssociationCollection):
"""Generic, converting, set-to-set proxy."""
- def __init__(self, lazy_collection, creator, getter, setter):
- """Constructs an _AssociationSet.
-
- collection
- A callable returning a set-based collection of entities (usually an
- object attribute managed by a SQLAlchemy relation())
-
- creator
- A function that creates new target entities. Given one parameter:
- value. The assertion is assumed::
-
- obj = creator(somevalue)
- assert getter(obj) == somevalue
-
- getter
- A function. Given an associated object, return the 'value'.
-
- setter
- A function. Given an associated object and a value, store that
- value on the object.
-
- """
- self.lazy_collection = lazy_collection
- self.creator = creator
- self.getter = getter
- self.setter = setter
-
- col = property(lambda self: self.lazy_collection())
-
def _create(self, value):
return self.creator(value)
-from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
+from sqlalchemy.test.testing import eq_, assert_raises
+import copy
import gc
+import pickle
+
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.orm.collections import collection
def remove(self, obj):
del self[obj.foo]
+
class SetCollection(set):
pass
+
class ListCollection(list):
pass
+
class ObjectCollection(object):
def __init__(self):
self.values = list()
def __iter__(self):
return iter(self.values)
+
+class Parent(object):
+ kids = association_proxy('children', 'name')
+ def __init__(self, name):
+ self.name = name
+
+class Child(object):
+ def __init__(self, name):
+ self.name = name
+
+class KVChild(object):
+ def __init__(self, name, value):
+ self.name = name
+ self.value = value
+
class _CollectionOperations(TestBase):
def setup(self):
collection_class = self.collection_class
metadata.create_all()
parents.insert().execute(name='p1')
- class Parent(object):
- kids = association_proxy('children', 'name')
- def __init__(self, name):
- self.name = name
-
- class Child(object):
- def __init__(self, name):
- self.name = name
-
- mapper(Parent, parents, properties=dict(children=relation(Child)))
- mapper(Child, children)
self.metadata = metadata
- self.Parent = Parent
-
+ self.parents = parents
+ self.children = children
+
def teardown(self):
self.metadata.drop_all()
+ clear_mappers()
def test_weak_identity_map(self):
+ mapper(Parent, self.parents, properties=dict(children=relation(Child)))
+ mapper(Child, self.children)
+
session = create_session(weak_identity_map=True)
def add_child(parent_name, child_name):
- parent = (session.query(self.Parent).
+ parent = (session.query(Parent).
filter_by(name=parent_name)).one()
parent.kids.append(child_name)
add_child('p1', 'c2')
session.flush()
- p = session.query(self.Parent).filter_by(name='p1').one()
+ p = session.query(Parent).filter_by(name='p1').one()
assert set(p.kids) == set(['c1', 'c2']), p.kids
def test_copy(self):
- import copy
- p = self.Parent('p1')
+ mapper(Parent, self.parents, properties=dict(children=relation(Child)))
+ mapper(Child, self.children)
+
+ p = Parent('p1')
p.kids.extend(['c1', 'c2'])
p_copy = copy.copy(p)
del p
assert set(p_copy.kids) == set(['c1', 'c2']), p.kids
+ def test_pickle_list(self):
+ mapper(Parent, self.parents, properties=dict(children=relation(Child)))
+ mapper(Child, self.children)
+
+ p = Parent('p1')
+ p.kids.extend(['c1', 'c2'])
+
+ r1 = pickle.loads(pickle.dumps(p))
+ assert r1.kids == ['c1', 'c2']
+ r2 = pickle.loads(pickle.dumps(p.kids))
+ assert r2 == ['c1', 'c2']
+
+ def test_pickle_set(self):
+ mapper(Parent, self.parents, properties=dict(children=relation(Child, collection_class=set)))
+ mapper(Child, self.children)
+
+ p = Parent('p1')
+ p.kids.update(['c1', 'c2'])
+
+ r1 = pickle.loads(pickle.dumps(p))
+ assert r1.kids == set(['c1', 'c2'])
+
+ r2 = pickle.loads(pickle.dumps(p.kids))
+ assert r2 == set(['c1', 'c2'])
+
+ def test_pickle_dict(self):
+ mapper(Parent, self.parents, properties=dict(
+ children=relation(KVChild, collection_class=collections.mapped_collection(PickleKeyFunc('name')))
+ ))
+ mapper(KVChild, self.children)
+
+ p = Parent('p1')
+ p.kids.update({'c1':'v1', 'c2':'v2'})
+ assert p.kids == {'c1':'c1', 'c2':'c2'}
+
+ r1 = pickle.loads(pickle.dumps(p))
+ assert r1.kids == {'c1':'c1', 'c2':'c2'}
+
+ r2 = pickle.loads(pickle.dumps(p.kids))
+ assert r2 == {'c1':'c1', 'c2':'c2'}
+
+class PickleKeyFunc(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __call__(self, obj):
+ return getattr(obj, self.name)
\ No newline at end of file