backref vs. back_populate

2,152 views
Skip to first unread message

fogat...@googlemail.com

unread,
Oct 16, 2009, 4:43:18 AM10/16/09
to sqlalchemy
Hello!

From reading the docs, I assumed that using "backref" on the child's
orm.relation declaration in a 1:n relationship is equivalent to using
"back_populates" with explicit orm.relation declarations on both ends.
However, it turns out that I get double insertions when using
back_populates - so, in the following code

p = Parent('parent1')
c = Child('child1')
c.parent = p
p.children.append(c)

p's children will contain c *twice*. Is this a bug, or am I reading
the docs wrong?

I'm grateful for any insights in this matter,

Oliver

=============
Class definitions:

class Child(object):
def __init__(self, name):
self.name = name
self.id = None
self.parent = None

class Parent(object):
def __init__(self, name):
self.name = name
self.id = None
self.children = []


Table definitions:

parent_table = sa.Table('parent', metadata,
sa.Column('parent_id', sa.types.Integer,
primary_key=True),
sa.Column('parent_name', sa.types.String,
nullable=False),
)

child_table = sa.Table('child', metadata,
sa.Column('child_id', sa.types.Integer,
primary_key=True),
sa.Column('child_name', sa.types.String,
nullable=False),
sa.Column('parent_id', sa.types.Integer,
sa.ForeignKey
(parent_table.c.parent_id),
nullable=False),
)

Mappers using backref:

orm.mapper(Parent, parent_table,
properties=
dict(name=orm.synonym('parent_name'),
id=orm.synonym('parent_id'),
)
)
orm.mapper(Child, child_table,
properties=
dict(parent=orm.relation(Parent,
uselist=False,
backref='parent'
),
name=orm.synonym('child_name'),
id=orm.synonym('child_id')
)
)

Mappers using back_populates:

orm.mapper(Parent, parent_table,
properties=
dict(name=orm.synonym('parent_name'),
id=orm.synonym('parent_id'),
children=orm.relation(Child,
back_populates='parent',
),
)
)
orm.mapper(Child, child_table,
properties=
dict(parent=orm.relation(Parent,
uselist=False,
back_populates='children',
),
name=orm.synonym('child_name'),
id=orm.synonym('child_id')
)
)

Michael Bayer

unread,
Oct 16, 2009, 6:50:15 AM10/16/09
to sqlal...@googlegroups.com

On Oct 16, 2009, at 4:43 AM, fogat...@googlemail.com wrote:

>
> Hello!
>
> From reading the docs, I assumed that using "backref" on the child's
> orm.relation declaration in a 1:n relationship is equivalent to using
> "back_populates" with explicit orm.relation declarations on both ends.
> However, it turns out that I get double insertions when using
> back_populates - so, in the following code
>
> p = Parent('parent1')
> c = Child('child1')
> c.parent = p
> p.children.append(c)
>
> p's children will contain c *twice*. Is this a bug, or am I reading
> the docs wrong?
>
> I'm grateful for any insights in this matter,

back_populates sets up the "synchronization" of the two directions,
which consists of an event that performs the complimentary set/append
operation when an append/set occurs on the other side. So the
c.parent = p operation results in the "append" on the other side, and
vice versa.

in the case of your "backref" example, you set up the backref as
"parent" when i think you meant to say "children".

fogat...@googlemail.com

unread,
Oct 16, 2009, 11:11:24 AM10/16/09
to sqlalchemy


On Oct 16, 12:50 pm, Michael Bayer <mike...@zzzcomputing.com> wrote:
> On Oct 16, 2009, at 4:43 AM, fogathm...@googlemail.com wrote:
>
>
>
>
>
> > Hello!
>
> > From reading the docs, I assumed that using "backref" on the child's
> > orm.relation declaration in a 1:n relationship is equivalent to using
> > "back_populates" with explicit orm.relation declarations on both ends.
> > However, it turns out that I get double insertions when using
> > back_populates - so, in the following code
>
> > p = Parent('parent1')
> > c = Child('child1')
> > c.parent = p
> > p.children.append(c)
>
> > p's children will contain c *twice*. Is this a bug, or am I reading
> > the docs wrong?
>
> > I'm grateful for any insights in this matter,
>
> back_populates sets up the "synchronization" of the two directions,  
> which consists of an event that performs the complimentary set/append  
> operation when an append/set occurs on the other side.   So the  
> c.parent = p operation results in the "append" on the other side, and  
> vice versa.

Ah, thanks, that makes sense. So I was wrong in assuming the two forms
were equivalent - backref merely allows you to access a relation from
the other side, whereas back_populates performs synchronization on
both ends. OK, I will keep that in mind from now on :-)

>
> in the case of your "backref" example, you set up the backref as  
> "parent" when i think you meant to say "children".
>

Yes, of course, sorry for the confusion.

Michael Bayer

unread,
Oct 16, 2009, 11:18:06 AM10/16/09
to sqlal...@googlegroups.com
fogat...@googlemail.com wrote:
>
> Ah, thanks, that makes sense. So I was wrong in assuming the two forms
> were equivalent - backref merely allows you to access a relation from
> the other side, whereas back_populates performs synchronization on
> both ends. OK, I will keep that in mind from now on :-)

ah no, they are really the same thing. the backref does the automatic
population in the other direction too. the backref argument just makes
the relation() build another relation() for you, and calls back_populates
on both. its strictly two different ways of configuring the same thing.

fogat...@googlemail.com

unread,
Oct 16, 2009, 11:40:47 AM10/16/09
to sqlalchemy


On Oct 16, 5:18 pm, "Michael Bayer" <mike...@zzzcomputing.com> wrote:
> fogathm...@googlemail.com wrote:
>
> > Ah, thanks, that makes sense. So I was wrong in assuming the two forms
> > were equivalent - backref merely allows you to access a relation from
> > the other side, whereas back_populates performs synchronization on
> > both ends. OK, I will keep that in mind from now on :-)
>
> ah no, they are really the same thing.  the backref does the automatic
> population in the other direction too.    the backref argument just makes
> the relation() build another relation() for you, and calls back_populates
> on both.    its strictly two different ways of configuring the same thing.
>

Argh. Thanks for not giving up on me - I had indeed run my code with
the wrong argument to backref and that was the reason for why I was
getting different results.

In the end, it is comforting that I have *not* misread the docs :-)
It seems I just have to figure out a consistent way to avoid those
double-insertions.
Reply all
Reply to author
Forward
0 new messages