ManyToMany relationships

38 views
Skip to first unread message

Felix Schwarz

unread,
Oct 31, 2007, 4:05:00 AM10/31/07
to sqle...@googlegroups.com
Hi,

this time, I don't have a problem to solve - in fact I'm puzzled why there is none ;-)

I like to build a graph:
----------------------------------------------------------------------------------
class Node(Entity):
name = Field(Unicode(200), required=True)
parents = ManyToMany('Node')
children = ManyToMany('Node')
using_options(tablename='nodes')

def __repr__(self):
return self.name
----------------------------------------------------------------------------------

And it just works... :-o
When I declare nodes as children of a given node, I can retrieve the original node by
examining parents on the children...

Now, I like to know if someone sees a problem with the code above. Will it break in
some situations? (Would be handy to know it before I built it into my application...)
How does Elixir figure out the inverse relationships? Does anyone know if this code
is non-portable between databases?

Thank you very much :-)
fs

Gaetan de Menten

unread,
Oct 31, 2007, 4:51:35 AM10/31/07
to sqle...@googlegroups.com
On 10/31/07, Felix Schwarz <felix....@web.de> wrote:
>
> Hi,
>
> this time, I don't have a problem to solve - in fact I'm puzzled why there is none ;-)
>
> I like to build a graph:
> ----------------------------------------------------------------------------------
> class Node(Entity):
> name = Field(Unicode(200), required=True)
> parents = ManyToMany('Node')
> children = ManyToMany('Node')
> using_options(tablename='nodes')
>
> def __repr__(self):
> return self.name
> ----------------------------------------------------------------------------------
>
> And it just works... :-o
> When I declare nodes as children of a given node, I can retrieve the original node by
> examining parents on the children...
>
> Now, I like to know if someone sees a problem with the code above. Will it break in
> some situations? (Would be handy to know it before I built it into my application...)

It might break if you inverse the fields order after creating the
table. This is a problem with Elixir in that the order you declare
fields in a bidirectional ManyToMany self-referential relationship is
significant. If you could add a ticket for this in the trac, that'd be
nice, so that I don't forget to fix that eventually.

> How does Elixir figure out the inverse relationships?

This is actually something I planned to document but never took the
time to. It looks for a matching relationship on the target entity. A
ManyToOne match as inverse of a OneToMany and vice versa, and a
ManyToMany match as inverse as a ManyToMany. It also pays attention
whether you specified an explicit inverse, and checks that each
relationship has only one inverse (if it finds more than one, it
raises an exception).

class Node(Entity):
name = Field(Unicode(200), required=True)

parents = ManyToMany('Node', inverse='children')
children = ManyToMany('Node', inverse='parents')
using_options(tablename='nodes')

> Does anyone know if this code
> is non-portable between databases?

Should be fine. The only problem that I know of is that the generated
many_to_many tables tend to have *very* long names and some database
do not accept such long names. But if you specify the tablename
manually you won't run into that problem:

class Node(Entity):
...
parents = ManyToMany('Node', tablename='node_children')
children = ManyToMany('Node', tablename='node_children')
using_options(tablename='nodes')

In that case, please specify the table name on both side of the
relationship (I think I remember it breaks otherwise, but I'd have to
look at the code to be sure).

--
Gaëtan de Menten
http://openhex.org

Felix Schwarz

unread,
Oct 31, 2007, 5:43:01 AM10/31/07
to sqle...@googlegroups.com

Gaetan de Menten wrote:
> It might break if you inverse the fields order after creating the
> table. This is a problem with Elixir in that the order you declare
> fields in a bidirectional ManyToMany self-referential relationship is
> significant. If you could add a ticket for this in the trac, that'd be
> nice, so that I don't forget to fix that eventually.

[x] done, http://elixir.ematia.de/trac/ticket/19

> class Node(Entity):
> name = Field(Unicode(200), required=True)
> parents = ManyToMany('Node', inverse='children')
> children = ManyToMany('Node', inverse='parents')
> using_options(tablename='nodes')

Ah! Now I see that the inverse statement came from SQLAlchemy which is why I
did not find out when looking at the docs...

Thank you very much for your detailed explanations - first class support :-))

fs

Gaetan de Menten

unread,
Oct 31, 2007, 6:18:00 AM10/31/07
to sqle...@googlegroups.com
On 10/31/07, Felix Schwarz <felix....@web.de> wrote:

> Gaetan de Menten wrote:
> > It might break if you inverse the fields order after creating the
> > table. This is a problem with Elixir in that the order you declare
> > fields in a bidirectional ManyToMany self-referential relationship is
> > significant. If you could add a ticket for this in the trac, that'd be
> > nice, so that I don't forget to fix that eventually.
>
> [x] done, http://elixir.ematia.de/trac/ticket/19

Thanks. Note that contrary to what you put in the ticket, the problem
will be present whether the "inverse" keyword is missing or not.

> > class Node(Entity):
> > name = Field(Unicode(200), required=True)
> > parents = ManyToMany('Node', inverse='children')
> > children = ManyToMany('Node', inverse='parents')
> > using_options(tablename='nodes')
>
> Ah! Now I see that the inverse statement came from SQLAlchemy which is why I
> did not find out when looking at the docs...

Hmmm no. :) The backref argument come from SQLAlchemy, but this is not
the same. The inverse argument is Elixir specific, and is documented
(though not very prominently):
http://elixir.ematia.de/apidocs/elixir.relationships.html. We should
probably add a reference to that in the tutorial, I guess.

> Thank you very much for your detailed explanations - first class support :-))

Your welcome.

Felix Schwarz

unread,
Oct 31, 2007, 7:04:20 AM10/31/07
to sqle...@googlegroups.com
Gaetan de Menten wrote:
>> Ah! Now I see that the inverse statement came from SQLAlchemy which is why I
>> did not find out when looking at the docs...
>
> Hmmm no. :) The backref argument come from SQLAlchemy, but this is not
> the same. The inverse argument is Elixir specific, and is documented
> (though not very prominently):
> http://elixir.ematia.de/apidocs/elixir.relationships.html. We should
> probably add a reference to that in the tutorial, I guess.

I think it needs to be documented in the list of arguments for every relationship on
http://elixir.ematia.de/apidocs/elixir.relationships.html.

e.g.:
"In addition to keyword arguments inherited from SQLAlchemy, ManyToMany
relationships accept the following optional (keyword) arguments:"
...
(lists "tablename", "remote_side", "local_side" and "order_by" but not inverse")

fs

Gaetan de Menten

unread,
Oct 31, 2007, 7:17:04 AM10/31/07
to sqle...@googlegroups.com
On 10/31/07, Felix Schwarz <felix....@web.de> wrote:
>

I usually don't like to duplicate stuff, but I guess you're right.
Would you care to make a patch?

Reply all
Reply to author
Forward
0 new messages