"""manages the value of a particular scalar attribute on a particular object instance."""
# make our own NONE to distinguish from "None"
NONE = object()
- def __init__(self, obj, key, **kwargs):
+ def __init__(self, obj, key, backrefmanager=None, **kwargs):
self.obj = obj
self.key = key
self.orig = PropHistory.NONE
+ self.backrefmanager = backrefmanager
def gethistory(self, *args, **kwargs):
return self
def history_contains(self, obj):
raise ("assigning a list to scalar property '%s' on '%s' instance %d" % (self.key, self.obj.__class__.__name__, id(self.obj)))
self.orig = self.obj.__dict__.get(self.key, None)
self.obj.__dict__[self.key] = value
+ if self.backrefmanager is not None and self.orig is not value:
+ self.backrefmanager.set(self.obj, value, self.orig)
def delattr(self):
self.orig = self.obj.__dict__.get(self.key, None)
self.obj.__dict__[self.key] = None
+ if self.backrefmanager is not None:
+ self.backrefmanager.set(self.obj, None, self.orig)
def rollback(self):
if self.orig is not PropHistory.NONE:
self.obj.__dict__[self.key] = self.orig
class ListElement(util.HistoryArraySet):
"""manages the value of a particular list-based attribute on a particular object instance."""
- def __init__(self, obj, key, data=None, **kwargs):
+ def __init__(self, obj, key, data=None, backrefmanager=None, **kwargs):
self.obj = obj
self.key = key
+ self.backrefmanager = backrefmanager
# if we are given a list, try to behave nicely with an existing
# list that might be set on the object already
try:
res = util.HistoryArraySet._setrecord(self, item)
if res:
self.list_value_changed(self.obj, self.key, item, self, False)
+ if self.backrefmanager is not None:
+ self.backrefmanager.append(self.obj, item)
return res
def _delrecord(self, item):
res = util.HistoryArraySet._delrecord(self, item)
if res:
self.list_value_changed(self.obj, self.key, item, self, True)
+ if self.backrefmanager is not None:
+ self.backrefmanager.delete(self.obj, item)
return res
class CallableProp(object):
def rollback(self):
pass
+class BackrefManager(object):
+ def __init__(self, key):
+ self.key = key
+ def append(self, parent, child):
+ pass
+ def delete(self, parent, child):
+ pass
+ def set(self, parent, child, oldchild):
+ pass
+
+
+class ListBackrefManager(BackrefManager):
+ def append(self, parent, child):
+ getattr(child, self.key).append(parent)
+ def delete(self, parent, child):
+ getattr(child, self.key).remove(parent)
+
+class OneToManyBackrefManager(BackrefManager):
+ def append(self, parent, child):
+ setattr(child, self.key, parent)
+ def delete(self, parent, child):
+ setattr(child, self.key, None)
+
+class ManyToOneBackrefManager(BackrefManager):
+ def set(self, parent, child, oldchild):
+ if oldchild is not None:
+ try:
+ getattr(oldchild, self.key).remove(parent)
+ except:
+ print "wha? oldchild is ", repr(oldchild)
+ if child is not None:
+ getattr(child, self.key).append(parent)
class AttributeManager(object):
"""maintains a set of per-attribute callable/history manager objects for a set of objects."""
self.assert_(u.user_id == 7 and u.user_name == 'john' and u.addresses[0].email_address == 'lala@123.com')
self.assert_(len(u.addresses.unchanged_items()) == 1)
+ def testbackref(self):
+ class Student(object):pass
+ class Course(object):pass
+ manager = attributes.AttributeManager()
+ manager.register_attribute(Student, 'courses', uselist=True, backrefmanager=attributes.ListBackrefManager('students'))
+ manager.register_attribute(Course, 'students', uselist=True, backrefmanager=attributes.ListBackrefManager('courses'))
+
+ s = Student()
+ c = Course()
+ s.courses.append(c)
+ self.assert_(c.students == [s])
+ s.courses.remove(c)
+ self.assert_(c.students == [])
+
+ (s1, s2, s3) = (Student(), Student(), Student())
+ c.students = [s1, s2, s3]
+ self.assert_(s2.courses == [c])
+ self.assert_(s1.courses == [c])
+ s1.courses.remove(c)
+ self.assert_(c.students == [s2,s3])
+
+
+ class Post(object):pass
+ class Blog(object):pass
+
+ manager.register_attribute(Post, 'blog', uselist=False, backrefmanager=attributes.ManyToOneBackrefManager('posts'))
+ manager.register_attribute(Blog, 'posts', uselist=True, backrefmanager=attributes.OneToManyBackrefManager('blog'))
+ b = Blog()
+ (p1, p2, p3) = (Post(), Post(), Post())
+ b.posts.append(p1)
+ b.posts.append(p2)
+ b.posts.append(p3)
+ self.assert_(b.posts == [p1, p2, p3])
+ self.assert_(p2.blog is b)
+
+ p3.blog = None
+ self.assert_(b.posts == [p1, p2])
+ p4 = Post()
+ p4.blog = b
+ self.assert_(b.posts == [p1, p2, p4])
+
+
+
if __name__ == "__main__":
unittest.main()