1. Michael Bayer
  2. sqlalchemy


Issue #73 resolved

simplify using backrefs on self-referring mappers

Michael Bayer
repo owner created an issue

this script is a regular parent/child tree node test. but we want "father" and "childs" to have a backref relationship. self-referring mappers, when you create the "many-to-one" relationship of the parent of a node, needs an explicit "foreignkey" parameter to give it a clue which relational direction we are dealing with. but when you define the "many-to-one" side via a backref, that relational foreignkey doesnt get sent.

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,
     # this one works, the 'childs' backref comes out ok
     'father':relation(Tree, foreignkey=table.c.id,primaryjoin=table.c.father_id==table.c.id,  backref='childs')},

     # this one doesnt, the "father" backref does not figure out that its "uselist=False"
     # 'childs':relation(Tree, foreignkey=table.c.father_id, primaryjoin=table.c.father_id==table.c.id,  backref='father')},

root = Tree('root')
child1 = Tree('child1', root)
child2 = Tree('child2', root)
child3 = Tree('child3', child1)


print root.childs
print child1.childs
print child2.childs
print child2.father
print child3.father

users should have a clear path to creating mappers like this. you can, instead of using 'backref="foo"', set up the backreference relationship explicitly by defining both the one-to-many and the many-to-one relationships explicitly, and then adding "attributeext=attributes.GenericBackrefExtension(<backrefname>)". so theres ways to make backrefernces more methodically. but we should make those ways simpler, more obvious, and better documented.

Comments (1)

  1. Michael Bayer reporter

    committed in changeset:1107 a new backref() function that takes the keyname as well as **kwargs to use in the new backref relation. this can be used in place of "backref='name'", i.e.

        backref = backref('mybackref', uselist=False, lazy=False, foreignkey=mytable.c.foo)
  2. Log in to comment