OK, for this one, the code that would fully simulate "backref" is this:

    m2.add_property(
        't1', rdb.relation(m1, attributeext=attributes.MTOBackrefExtension('t2s'))
        )
    m1.add_property(
        't2s', rdb.relation(m2, attributeext=attributes.OTMBackrefExtension('t1'))
        )

which is defining some extra goodies for how the 'properties' package instructs the 'attributes' package how to manage the 't1' and 't2s' attributes.  setting 't1' on a C2 object will append it to the list 't2s' on the C1 object being added. appending a C2 object to the 't1' list on a C1 object will automatically set the 't1' attribute on the C2 object.

the idea is that the mapper/properties system remains being primarily concerned with loading and saving objects, and the attribute enhancement handles the two-way relationship, as though the application were doing it anyway.  

you can see the two-way relationship happen without getting into any database / ORM stuff at all, just using the attributes package (heres part of the unit tests for it):

        class Post(object):pass
        class Blog(object):pass
       
        manager = attributes.AttributeManager()
        manager.register_attribute(Post, 'blog', uselist=False, extension=attributes.MTOBackrefExtension('posts'))
        manager.register_attribute(Blog, 'posts', uselist=True, extension=attributes.OTMBackrefExtension('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)

the full mindstorm that created this strategy is here:  



On Jan 19, 2006, at 5:37 PM, [EMAIL PROTECTED] wrote:

import sqlalchemy as rdb


USE_BACKREF = True


# Setup data

engine = rdb.create_engine('sqlite://', echo=True)


t1 = rdb.Table(

    't1', engine,

    rdb.Column('id', rdb.Integer, primary_key=True),

    )

t1.create()

class C1(object):

    pass

m1 = rdb.mapper(C1, t1)


t2 = rdb.Table(

    't2', engine,

    rdb.Column('id', rdb.Integer, primary_key=True),

    rdb.Column('t1id', rdb.Integer, rdb.ForeignKey(t1.c.id)),

    )

t2.create()

class C2(object):

    pass

m2 = rdb.mapper(C2, t2)


# Choice of backref here

if USE_BACKREF:

    m2.add_property(

        't1', rdb.relation(m1, backref='t2s')

        )

else:

    m2.add_property(

        't1', rdb.relation(m1)

        )

    m1.add_property(

        't2s', rdb.relation(m2)

        )


# Test

a = C1()

a.id = 0

for i in xrange(10):

    b = C2()

    b.id = i

    b.t1 = a

rdb.objectstore.commit()

assert len(a.t2s) == 10


Reply via email to