return getattr(user_data, '_sa_adapter')
-class GenericBackrefExtension(interfaces.AttributeExtension):
- """An extension which synchronizes a two-way relationship.
+def backref_listeners(attribute, key, uselist):
+ """Apply listeners to synchronize a two-way relationship."""
- A typical two-way relationship is a parent object containing a list of
- child objects, where each child object references the parent. The other
- are two objects which contain scalar references to each other.
-
- """
-
- active_history = False
-
- def __init__(self, key):
- self.key = key
-
- def set(self, state, child, oldchild, initiator):
+ def set_(state, child, oldchild, initiator):
if oldchild is child:
return child
# present when updating via a backref.
old_state, old_dict = instance_state(oldchild),\
instance_dict(oldchild)
- impl = old_state.get_impl(self.key)
+ impl = old_state.get_impl(key)
try:
impl.remove(old_state,
old_dict,
if child is not None:
child_state, child_dict = instance_state(child),\
instance_dict(child)
- child_state.get_impl(self.key).append(
+ child_state.get_impl(key).append(
child_state,
child_dict,
state.obj(),
passive=PASSIVE_NO_FETCH)
return child
- def append(self, state, child, initiator):
+ def append(state, child, initiator):
child_state, child_dict = instance_state(child), \
instance_dict(child)
- child_state.get_impl(self.key).append(
+ child_state.get_impl(key).append(
child_state,
child_dict,
state.obj(),
passive=PASSIVE_NO_FETCH)
return child
- def remove(self, state, child, initiator):
+ def remove(state, child, initiator):
if child is not None:
child_state, child_dict = instance_state(child),\
instance_dict(child)
- child_state.get_impl(self.key).remove(
+ child_state.get_impl(key).remove(
child_state,
child_dict,
state.obj(),
initiator,
passive=PASSIVE_NO_FETCH)
-
-
+
+ if uselist:
+ event.listen(append, "on_append", attribute, retval=False, raw=True)
+ else:
+ event.listen(set_, "on_set", attribute, retval=False, raw=True)
+ # TODO: need coverage in test/orm/ of remove event
+ event.listen(remove, "on_remove", attribute, retval=False, raw=True)
+
class History(tuple):
"""A 3-tuple of added, unchanged and deleted values,
representing the changes which have occured on an instrumented
comparator = kw.pop('comparator', None)
parententity = kw.pop('parententity', None)
doc = kw.pop('doc', None)
- register_descriptor(class_, key,
+ desc = register_descriptor(class_, key,
comparator, parententity, doc=doc)
register_attribute_impl(class_, key, **kw)
+ return desc
def register_attribute_impl(class_, key,
uselist=False, callable_=None,
useobject=False, mutable_scalars=False,
- impl_class=None, **kw):
+ impl_class=None, backref=None, **kw):
manager = manager_of_class(class_)
if uselist:
impl = ScalarAttributeImpl(class_, key, callable_, dispatch, **kw)
manager[key].impl = impl
+
+ if backref:
+ backref_listeners(manager[key], backref, uselist)
manager.post_configure_attribute(key)
descriptor.__doc__ = doc
manager.instrument_attribute(key, descriptor)
+ return descriptor
def unregister_attribute(class_, key):
manager_of_class(class_).uninstrument_attribute(key)
return [bar1, bar2, bar3]
attributes.register_attribute(Foo, 'bars', uselist=True, callable_=lambda o:func1, useobject=True, extension=[ReceiveEvents()])
- attributes.register_attribute(Bar, 'foos', uselist=True, useobject=True, extension=[attributes.GenericBackrefExtension('bars')])
+ attributes.register_attribute(Bar, 'foos', uselist=True, useobject=True, backref="bars")
x = Foo()
assert_raises(AssertionError, Bar(id=4).foos.append, x)
# set up instrumented attributes with backrefs
attributes.register_attribute(Post, 'blog', uselist=False,
- extension=attributes.GenericBackrefExtension('posts'),
+ backref='posts',
trackparent=True, useobject=True)
attributes.register_attribute(Blog, 'posts', uselist=True,
- extension=attributes.GenericBackrefExtension('blog'),
+ backref='blog',
trackparent=True, useobject=True)
# create objects as if they'd been freshly loaded from the database (without history)
instrumentation.register_class(Student)
instrumentation.register_class(Course)
attributes.register_attribute(Student, 'courses', uselist=True,
- extension=attributes.GenericBackrefExtension('students'
- ), useobject=True)
+ backref="students", useobject=True)
attributes.register_attribute(Course, 'students', uselist=True,
- extension=attributes.GenericBackrefExtension('courses'
- ), useobject=True)
+ backref="courses", useobject=True)
s = Student()
c = Course()
instrumentation.register_class(Post)
instrumentation.register_class(Blog)
attributes.register_attribute(Post, 'blog', uselist=False,
- extension=attributes.GenericBackrefExtension('posts'),
+ backref='posts',
trackparent=True, useobject=True)
attributes.register_attribute(Blog, 'posts', uselist=True,
- extension=attributes.GenericBackrefExtension('blog'),
+ backref='blog',
trackparent=True, useobject=True)
b = Blog()
(p1, p2, p3) = (Post(), Post(), Post())
class Jack(object):pass
instrumentation.register_class(Port)
instrumentation.register_class(Jack)
- attributes.register_attribute(Port, 'jack', uselist=False,
- extension=attributes.GenericBackrefExtension('port'),
- useobject=True)
+
+ attributes.register_attribute(Port, 'jack', uselist=False,
+ useobject=True, backref="port")
+
attributes.register_attribute(Jack, 'port', uselist=False,
- extension=attributes.GenericBackrefExtension('jack'),
- useobject=True)
+ useobject=True, backref="jack")
+
+
p = Port()
j = Jack()
p.jack = j
instrumentation.register_class(Child)
instrumentation.register_class(SubChild)
attributes.register_attribute(Parent, 'child', uselist=False,
- extension=attributes.GenericBackrefExtension('parent'),
+ backref="parent",
parent_token = p_token,
useobject=True)
attributes.register_attribute(Child, 'parent', uselist=False,
- extension=attributes.GenericBackrefExtension('child'),
+ backref="child",
parent_token = c_token,
useobject=True)
attributes.register_attribute(SubChild, 'parent',
uselist=False,
- extension=attributes.GenericBackrefExtension('child'),
+ backref="child",
parent_token = c_token,
useobject=True)
instrumentation.register_class(SubParent)
instrumentation.register_class(Child)
attributes.register_attribute(Parent, 'children', uselist=True,
- extension=attributes.GenericBackrefExtension('parent'),
+ backref='parent',
parent_token = p_token,
useobject=True)
attributes.register_attribute(SubParent, 'children', uselist=True,
- extension=attributes.GenericBackrefExtension('parent'),
+ backref='parent',
parent_token = p_token,
useobject=True)
attributes.register_attribute(Child, 'parent', uselist=False,
- extension=attributes.GenericBackrefExtension('children'),
+ backref='children',
parent_token = c_token,
useobject=True)
instrumentation.register_class(Post)
instrumentation.register_class(Blog)
- attributes.register_attribute(Post, 'blog', uselist=False, extension=attributes.GenericBackrefExtension('posts'), trackparent=True, useobject=True)
- attributes.register_attribute(Blog, 'posts', uselist=True, extension=attributes.GenericBackrefExtension('blog'), callable_=lazy_posts, trackparent=True, useobject=True)
+ attributes.register_attribute(Post, 'blog', uselist=False, backref='posts', trackparent=True, useobject=True)
+ attributes.register_attribute(Blog, 'posts', uselist=True, backref='blog', callable_=lazy_posts, trackparent=True, useobject=True)
def test_lazy_add(self):
global lazy_load
instrumentation.register_class(Foo)
instrumentation.register_class(Bar)
- attributes.register_attribute(Foo, 'bars', uselist=True, extension=attributes.GenericBackrefExtension('foo'), trackparent=True, useobject=True)
- attributes.register_attribute(Bar, 'foo', uselist=False, extension=attributes.GenericBackrefExtension('bars'), trackparent=True, useobject=True)
+ attributes.register_attribute(Foo, 'bars', uselist=True, backref='foo', trackparent=True, useobject=True)
+ attributes.register_attribute(Bar, 'foo', uselist=False, backref='bars', trackparent=True, useobject=True)
f1 = Foo()
b1 = Bar()
instrumentation.register_class(Foo)
instrumentation.register_class(Bar)
- attributes.register_attribute(Foo, 'bars', uselist=True, extension=attributes.GenericBackrefExtension('foo'), trackparent=True, callable_=lazyload, useobject=True)
- attributes.register_attribute(Bar, 'foo', uselist=False, extension=attributes.GenericBackrefExtension('bars'), trackparent=True, useobject=True)
+ attributes.register_attribute(Foo, 'bars', uselist=True, backref='foo', trackparent=True, callable_=lazyload, useobject=True)
+ attributes.register_attribute(Bar, 'foo', uselist=False, backref='bars', trackparent=True, useobject=True)
bar1, bar2, bar3, bar4 = [Bar(id=1), Bar(id=2), Bar(id=3), Bar(id=4)]
lazy_load = [bar1, bar2, bar3]