]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
added backref() function, allows the creation of a backref where you also send keywor...
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 7 Mar 2006 03:14:08 +0000 (03:14 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 7 Mar 2006 03:14:08 +0000 (03:14 +0000)
examples/backref/backref_tree.py [new file with mode: 0644]
lib/sqlalchemy/mapping/__init__.py
lib/sqlalchemy/mapping/properties.py

diff --git a/examples/backref/backref_tree.py b/examples/backref/backref_tree.py
new file mode 100644 (file)
index 0000000..0959806
--- /dev/null
@@ -0,0 +1,41 @@
+from sqlalchemy import *
+import sqlalchemy.attributes as attributes
+
+engine = create_engine('sqlite://', echo=True)
+
+class Tree(object):
+    def __init__(self, name='', father=None):
+        self.name = name
+        self.father = father
+    def __str__(self):
+        return '<TreeNode: %s>' % self.name
+    def __repr__(self):
+        return self.__str__()
+        
+table = Table('tree', engine,
+              Column('id', Integer, primary_key=True),
+              Column('name', String(64), nullable=False),
+              Column('father_id', Integer, ForeignKey('tree.id'), nullable=True),)
+
+assign_mapper(Tree, table,
+              properties={
+     # set up a backref using a string
+     #'father':relation(Tree, foreignkey=table.c.id,primaryjoin=table.c.father_id==table.c.id,  backref='childs')},
+                
+     # or set up using the backref() function, which allows arguments to be passed
+     'childs':relation(Tree, foreignkey=table.c.father_id, primaryjoin=table.c.father_id==table.c.id,  backref=backref('father', uselist=False, foreignkey=table.c.id))},
+            )
+
+table.create()
+root = Tree('root')
+child1 = Tree('child1', root)
+child2 = Tree('child2', root)
+child3 = Tree('child3', child1)
+
+objectstore.commit()
+
+print root.childs
+print child1.childs
+print child2.childs
+print child2.father
+print child3.father
index 895b8b642c8d8c0ea8bee961de771a4543940480..e694d2917a8c40fe1630595c4f21d593b61d6f63 100644 (file)
@@ -19,7 +19,7 @@ from mapper import *
 from properties import *
 import mapper as mapperlib
 
-__all__ = ['relation', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer',
+__all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer',
         'mapper', 'clear_mappers', 'objectstore', 'sql', 'extension', 'class_mapper', 'object_mapper', 'MapperExtension',
         'assign_mapper', 'cascade_mappers'
         ]
@@ -39,7 +39,12 @@ def _relation_loader(mapper, secondary=None, primaryjoin=None, secondaryjoin=Non
     else:
         return EagerLoader(mapper, secondary, primaryjoin, secondaryjoin, **kwargs)
 
+def backref(name, **kwargs):
+    return BackRef(name, **kwargs)
+    
 def deferred(*columns, **kwargs):
+    """returns a DeferredColumnProperty, which indicates this object attributes should only be loaded 
+    from its corresponding table column when first accessed."""
     return DeferredColumnProperty(*columns, **kwargs)
     
 def mapper(class_, table=None, *args, **params):
index 617f3bbaf765acba56840e7efa0cca28bb35e8ff..6ca0a7641b98c818ae857b00ea44462e5fbb9946 100644 (file)
@@ -136,7 +136,10 @@ class PropertyLoader(MapperProperty):
             print "'use_alias' argument to relation() is deprecated.  eager loads automatically alias-ize tables now."
         self.order_by = order_by
         self.attributeext=attributeext
-        self.backref = backref
+        if isinstance(backref, str):
+            self.backref = BackRef(backref)
+        else:
+            self.backref = backref
         self.is_backref = is_backref
 
     def copy(self):
@@ -197,27 +200,13 @@ class PropertyLoader(MapperProperty):
             # if a backref name is defined, set up an extension to populate 
             # attributes in the other direction
             if self.backref is not None:
-                self.attributeext = attributes.GenericBackrefExtension(self.backref)
+                self.attributeext = self.backref.get_extension()
         
             # set our class attribute
             self._set_class_attribute(parent.class_, key)
 
             if self.backref is not None:
-                # try to set a LazyLoader on our mapper referencing the parent mapper
-                if not self.mapper.props.has_key(self.backref):
-                    if self.secondaryjoin is not None:
-                        # if setting up a backref to a many-to-many, reverse the order
-                        # of the "primary" and "secondary" joins
-                        pj = self.secondaryjoin
-                        sj = self.primaryjoin
-                    else:
-                        pj = self.primaryjoin
-                        sj = None
-                    self.mapper.add_property(self.backref, LazyLoader(self.parent, self.secondary, pj, sj, backref=self.key, is_backref=True));
-                else:
-                    # else set one of us as the "backreference"
-                    if not self.mapper.props[self.backref].is_backref:
-                        self.is_backref=True
+                self.backref.compile(self)
         elif not objectstore.global_attributes.is_class_managed(parent.class_, key):
             raise ArgumentError("Non-primary property created for attribute '%s' on class '%s', but that attribute is not managed! Insure that the primary mapper for this class defines this property" % (key, parent.class_.__name__))
 
@@ -816,7 +805,33 @@ class GenericOption(MapperOption):
         kwargs = util.constructor_args(oldprop)
         mapper.set_property(key, class_(**kwargs ))
 
-            
+class BackRef(object):
+    """stores the name of a backreference property as well as a relation (PropertyLoader),
+    used to construct more customized backrefs"""
+    def __init__(self, key, **kwargs):
+        self.key = key
+        self.kwargs = kwargs
+    def compile(self, prop):
+        # try to set a LazyLoader on our mapper referencing the parent mapper
+        if not prop.mapper.props.has_key(self.key):
+            if prop.secondaryjoin is not None:
+                # if setting up a backref to a many-to-many, reverse the order
+                # of the "primary" and "secondary" joins
+                pj = prop.secondaryjoin
+                sj = prop.primaryjoin
+            else:
+                pj = prop.primaryjoin
+                sj = None
+            relation = LazyLoader(prop.parent, prop.secondary, pj, sj, backref=prop.key, is_backref=True, **self.kwargs)
+            prop.mapper.add_property(self.key, relation);
+        else:
+            # else set one of us as the "backreference"
+            if not prop.mapper.props[self.key].is_backref:
+                prop.is_backref=True
+        
+    def get_extension(self):
+        return attributes.GenericBackrefExtension(self.key)
+        
 class EagerLazyOption(GenericOption):
     """an option that switches a PropertyLoader to be an EagerLoader or LazyLoader"""
     def __init__(self, key, toeager = True, **kwargs):