Problem with inheritence and foreign keys

2 views
Skip to first unread message

Paul Johnston

unread,
Jul 30, 2008, 9:48:11 AM7/30/08
to SQLElixir
Hi,

See this code:
http://dpaste.com/68432/

It causes the following error:
Traceback (most recent call last):
File "C:\test.py", line 25, in ?
print Foreign.q
AttributeError: type object 'Foreign' has no attribute 'q'

My reckoning is that this should work, and this is an Elixir bug. Not
going to be easy tracking it down though, any help would be
appreciated!

Paul

Gaetan de Menten

unread,
Jul 30, 2008, 10:56:12 AM7/30/08
to sqle...@googlegroups.com

Sorry, I don't have much time at the moment to help you fix it.

I can just tell you that it is a limitation in the
get_inverse_relation method in entity.py: it looks only in the current
entity relationship list and not in its parents. Should be easily
fixable, but I don't know if it will break something else, and I'm
also unsure whether this pattern is supported by SQLAlchemy or not.

As a workaround, putting the "foreign" relationship on the Subclasses
should work.

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

Gaetan de Menten

unread,
Jul 30, 2008, 11:06:26 AM7/30/08
to sqle...@googlegroups.com
On Wed, Jul 30, 2008 at 4:56 PM, Gaetan de Menten <gdem...@gmail.com> wrote:
> On Wed, Jul 30, 2008 at 3:48 PM, Paul Johnston <paul...@gmail.com> wrote:
>>
>> Hi,
>>
>> See this code:
>> http://dpaste.com/68432/
>>
>> It causes the following error:
>> Traceback (most recent call last):
>> File "C:\test.py", line 25, in ?
>> print Foreign.q
>> AttributeError: type object 'Foreign' has no attribute 'q'
>>
>> My reckoning is that this should work, and this is an Elixir bug. Not
>> going to be easy tracking it down though, any help would be
>> appreciated!
>
> Sorry, I don't have much time at the moment to help you fix it.
>
> I can just tell you that it is a limitation in the
> get_inverse_relation method in entity.py: it looks only in the current
> entity relationship list and not in its parents. Should be easily
> fixable, but I don't know if it will break something else, and I'm
> also unsure whether this pattern is supported by SQLAlchemy or not.

Nevermind what I just wrote. Since you specified inverse names, that
code shouldn't even be used.
The problem is probably coming from the way the relations/backrefs are
setup in relationships.py/Relationship.create_properties: I made an
assumption in there that a particular relationship would only ever
have one inverse. In your case, the "foreign" relationship has two.

Fixing this might not be easy... If you provide me with an example of
how SQLAlchemy would handle that case, I'll see if I can implement it
in Elixir next week.

Paul Johnston

unread,
Jul 31, 2008, 9:53:32 AM7/31/08
to SQLElixir
Hi,

If it helps, I'm only using "inverse" because when I didn't at first I
got a different error, that suggested I use it.

The workaround you suggested (putting the relation on each subclass,
not the baseclass) doesn't seem to help - code here http://dpaste.com/68669/

I had a go at coding this in pure-SA http://dpaste.com/68668/ Not
tested it much, but seems to work ok.

I wonder if this is something inside setup_all() getting confused by a
circular dependency. I'll have a look, but I'm gonna struggle.

Paul



On Jul 30, 4:06 pm, "Gaetan de Menten" <gdemen...@gmail.com> wrote:
> On Wed, Jul 30, 2008 at 4:56 PM, Gaetan de Menten <gdemen...@gmail.com> wrote:

Gaetan de Menten

unread,
Jul 31, 2008, 11:43:38 AM7/31/08
to sqle...@googlegroups.com
On Thu, Jul 31, 2008 at 3:53 PM, Paul Johnston <paul...@gmail.com> wrote:

> If it helps, I'm only using "inverse" because when I didn't at first I
> got a different error, that suggested I use it.

Yep, that's to be expected...

> The workaround you suggested (putting the relation on each subclass,
> not the baseclass) doesn't seem to help - code here http://dpaste.com/68669/

It does work... if you compile the mapper before trying to access one
of the backrefs (you didn't use any in your SA example).
Backref are generated automatically by Elixir, but are only available
after a mapper compilation. Usually the mapper is compiled
automatically when you do a query, or instanciate the class and so on
(I don't really know the conditions for the compilation).

Anyway, compiling one mapper manually, compiles them all, so simply
adding the following line after setup_all() solves your test case.

Base.mapper.compile()

> I had a go at coding this in pure-SA http://dpaste.com/68668/ Not
> tested it much, but seems to work ok.
>
> I wonder if this is something inside setup_all() getting confused by a
> circular dependency. I'll have a look, but I'm gonna struggle.
>

--

Paul Johnston

unread,
Aug 7, 2008, 4:52:43 AM8/7/08
to SQLElixir
Hi,

> It does work... if you compile the mapper before trying to access one
> of the backrefs

Aha! It does indeed work :-) Thank-you.

This has be working for now, but it would be great to have the
relation on the base class. I've raised ticket #62 to remind us about
this, although realistically I'm unlikely to be able to do much work
on it.

Thanks again for the top notch support.

Paul

Gaetan de Menten

unread,
Aug 7, 2008, 5:46:10 AM8/7/08
to sqle...@googlegroups.com

Thanks for the ticket. I'll need to fix the technical part of this
issue one day anyway because of other issues, but there is still
another issue about "let's not trick the user by providing
automatically something slightly different than usual because we can't
provide him with the usual stuff". See my comment in the ticket for
more info.

Paul Johnston

unread,
Aug 7, 2008, 11:23:06 AM8/7/08
to SQLElixir
Hi,

I know exactly what backrefs I want...

We have base class A with a relation to class B, and a sub-class C,
derived from A.
I want A to have a relation to B and the backref from B to be a
_polymorphic_ relation to A (and, in effect, it's subclasses)
I want C to have a relation to B and no backref

I don't think the desired behavior is unclear, it's just getting the
damn thing to work!

Paul

Gaetan de Menten

unread,
Aug 7, 2008, 12:37:34 PM8/7/08
to sqle...@googlegroups.com
On Thu, Aug 7, 2008 at 5:23 PM, Paul Johnston <paul...@gmail.com> wrote:
>
> Hi,
>
> I know exactly what backrefs I want...
>
> We have base class A with a relation to class B, and a sub-class C,
> derived from A.
> I want A to have a relation to B and the backref from B to be a
> _polymorphic_ relation to A (and, in effect, it's subclasses)
> I want C to have a relation to B and no backref

This should already work as-is:

class B(Entity):
a = OneToOne('A')

class A(Entity):
b = ManyToOne('B')

class C1(A):
pass

class C2(A):
pass

I haven't tested it, but I believe it should work. This is not what
you initially described though. The difference lies in B, which has a
polymorphic relation to the parent class A instead of two non
polymorphic relationships to the subclasses of A.

> I don't think the desired behavior is unclear, it's just getting the
> damn thing to work!

--

Paul Johnston

unread,
Aug 8, 2008, 5:34:18 AM8/8/08
to SQLElixir
Hi,

...
> This is not what you initially described though.

Ooops! Sorry, ignore me. :-)

Paul
Reply all
Reply to author
Forward
0 new messages