Discussion:
[Modeling-users] toOne relationship returns base class instead of subclass?
John Lenton
2004-07-22 15:16:39 UTC
Permalink
in the model

Person
/ \
|
PhysicalPerson <---->> Address

querying an Address for its PhysicalPerson returns Person instances,
which was unexpected (for me, at least). Is this correct, or is it a
bug?
--
John Lenton (***@gmail.com) -- Random fortune:
bash: fortune: command not found
Sebastien Bigaret
2004-07-23 11:04:02 UTC
Permalink
Post by John Lenton
in the model
Person
/ \
|
PhysicalPerson <---->> Address
querying an Address for its PhysicalPerson returns Person instances,
which was unexpected (for me, at least). Is this correct, or is it a
bug?
If you mean that, having an address 'addr', addr.getPerson() has class
'Person', yes, this is intended, as long as addr.getPerson().isFault()
is True. As soon as you'll need an addr's person property the fault will
be fired and you'll get the correct class for addr.getPerson().


Now you may wonder why this is like this. We discussed this here:
https://sourceforge.net/mailarchive/forum.php?thread_id=3457563&forum_id=10674


See esp. the third post dated 2003-11-15 20:40. While the example taken
there involves a relationship pointing from a subclass to its root
class, you'll see that you get exactly the same kind of problem if you
add a class Photo such as: Photo <<---> Person


Now you'll see that the discussion "ended" there with an open
question, about if and how this could/should be changed. Time to
rediscuss this I guess, huh?


-- Sébastien.


PS: BTW the interesting methods on this topic:

DBChannel.fetchObject() calling DBContext.initializeObject(),
which in particular populates the object's toOne properties with
DBContext.faultForGlobalID().

and this particular behaviour is tested in:
test_EC_Global_Inheritance.test_02_toOneFault()
John Lenton
2004-07-23 11:47:04 UTC
Permalink
On 23 Jul 2004 15:02:00 +0200, Sebastien Bigaret
Post by Sebastien Bigaret
Post by John Lenton
in the model
Person
/ \
|
PhysicalPerson <---->> Address
querying an Address for its PhysicalPerson returns Person instances,
which was unexpected (for me, at least). Is this correct, or is it a
bug?
If you mean that, having an address 'addr', addr.getPerson() has class
'Person', yes, this is intended, as long as addr.getPerson().isFault()
is True. As soon as you'll need an addr's person property the fault will
be fired and you'll get the correct class for addr.getPerson().
actually, what's biting me is this (I'm translating from a different
model into this one that is similar to the example, for clarity and
Post by Sebastien Bigaret
Post by John Lenton
[i.getPerson().getId() for i in ec.fetch('Address')]
[4, 5]
Post by Sebastien Bigaret
Post by John Lenton
ec.fetch('Address', 'person.id == 4')
[]

in trying to simplify this last time I got the behaviour above, which
I misinterpreted as the same thing.
Post by Sebastien Bigaret
https://sourceforge.net/mailarchive/forum.php?thread_id=3457563&forum_id=10674
ok. Will read up---tomorrow (deadlines...!)
--
John Lenton (***@gmail.com) -- Random fortune:
bash: fortune: command not found
Sebastien Bigaret
2004-07-23 17:02:00 UTC
Permalink
John Lenton <***@gmail.com> wrote:
[...]
Post by John Lenton
actually, what's biting me is this (I'm translating from a different
model into this one that is similar to the example, for clarity and
[i.getPerson().getId() for i in ec.fetch('Address')]
[4, 5]
ec.fetch('Address', 'person.id == 4')
[]
in trying to simplify this last time I got the behaviour above, which
I misinterpreted as the same thing.
Quickly replying here: this is absolutely not normal, and it would help
a lot if you could provide a sample model, code and data (sal dump is
ok).

Or maybe you were trying something like this? ->
Post by John Lenton
[i.getPerson().getId() for i in ec.fetch('Address')]
[4, 5]
Post by John Lenton
ec.fetch('Person', id == 4')
[]
Post by John Lenton
ec.fetch('Person', id == 4', isDeep=1) # fetch the whole inheritce tree
[<PhysicalPerson instance at 0x...>]


-- Sébastien.
Sebastien Bigaret
2004-07-29 06:31:03 UTC
Permalink
Hi John,

Again, could you be a little more explicit, or exhibit a short
example? Sorry for getting back on this, but the only way I think this
can happen is the one I already posted (see below); if it's not the
case, this is a serious bug I definitely do not want to let escape.

Thanks for your help!

-- Sébastien.
Post by Sebastien Bigaret
[...]
Post by John Lenton
actually, what's biting me is this (I'm translating from a different
model into this one that is similar to the example, for clarity and
[i.getPerson().getId() for i in ec.fetch('Address')]
[4, 5]
ec.fetch('Address', 'person.id == 4')
[]
in trying to simplify this last time I got the behaviour above, which
I misinterpreted as the same thing.
Quickly replying here: this is absolutely not normal, and it would help
a lot if you could provide a sample model, code and data (sal dump is
ok).
Or maybe you were trying something like this? ->
Post by John Lenton
[i.getPerson().getId() for i in ec.fetch('Address')]
[4, 5]
Post by John Lenton
ec.fetch('Person', id == 4')
[]
Post by John Lenton
ec.fetch('Person', id == 4', isDeep=1) # fetch the whole inheritce tree
[<PhysicalPerson instance at 0x...>]
-- Sébastien.
John Lenton
2004-07-29 16:11:05 UTC
Permalink
On 29 Jul 2004 10:30:02 +0200, Sebastien Bigaret
Post by Sebastien Bigaret
Hi John,
Again, could you be a little more explicit, or exhibit a short
example? Sorry for getting back on this, but the only way I think this
can happen is the one I already posted (see below); if it's not the
case, this is a serious bug I definitely do not want to let escape.
sorry for not getting back to you. I have yet to sit down and
reproduce that with the "stock" modeling; I'll try to do it today. The
only difference between what I posted and what I actually ran was the
names of the entities; as I say, I'll try to get a full example to you
today.
--
John Lenton (***@gmail.com) -- Random fortune:
bash: fortune: command not found
John Lenton
2004-08-25 02:01:20 UTC
Permalink
On 29 Jul 2004 10:30:02 +0200, Sebastien Bigaret
Post by Sebastien Bigaret
Again, could you be a little more explicit, or exhibit a short
example? Sorry for getting back on this, but the only way I think this
can happen is the one I already posted (see below); if it's not the
case, this is a serious bug I definitely do not want to let escape.
sorry for the delay in answering, and the confusion when doing so. I
feel terribly embarrassed :(

HOWever, here goes.

With the attached model (which is ugly, and breaks some of your naming
conventions, but I just hacked out the attributes and renamed things
from the model being used by a team I'm helping to get up to speed),
Post by Sebastien Bigaret
from Modeling.EditingContext import EditingContext
import Store
ec=EditingContext()
ec.fetch('Address')[0]
<Address.Address object at 0x4071c74c>
Post by Sebastien Bigaret
a = _
p = ec.fetch('Person', isDeep=1)[0]
<Employee.Employee object at 0x4072d88c>
Post by Sebastien Bigaret
p = _
a.getPerson()
<Employee.Employee object at 0x4072d88c>
Post by Sebastien Bigaret
a.getPerson().getId()
1
Post by Sebastien Bigaret
a.isFault()
False
Post by Sebastien Bigaret
p.getAddress()
<Modeling.FaultHandler.AccessArrayFaultHandler instance at 0x40733a4c>
Post by Sebastien Bigaret
list(p.getAddress())
[<Address.Address object at 0x4071c74c>]
Post by Sebastien Bigaret
ec.fetch('Address', 'Person.id == 1')
[]
Post by Sebastien Bigaret
ec.fetch('Address', 'Person.id == 1', isDeep=1)
[]
--
John Lenton (***@gmail.com) -- Random fortune:
bash: fortune: command not found
Sebastien Bigaret
2004-08-26 17:47:33 UTC
Permalink
Post by John Lenton
On 29 Jul 2004 10:30:02 +0200, Sebastien Bigaret
Post by Sebastien Bigaret
Again, could you be a little more explicit, or exhibit a short
example? Sorry for getting back on this, but the only way I think this
can happen is the one I already posted (see below); if it's not the
case, this is a serious bug I definitely do not want to let escape.
sorry for the delay in answering, and the confusion when doing so. I
feel terribly embarrassed :(
Please don't feel embarrassed, that was worth waiting for it.
Post by John Lenton
HOWever, here goes.
With the attached model (which is ugly, and breaks some of your naming
conventions, but I just hacked out the attributes and renamed things
from the model being used by a team I'm helping to get up to speed),
Post by Sebastien Bigaret
from Modeling.EditingContext import EditingContext
import Store
ec=EditingContext()
ec.fetch('Address')[0]
<Address.Address object at 0x4071c74c>
Post by Sebastien Bigaret
a = _
p = ec.fetch('Person', isDeep=1)[0]
<Employee.Employee object at 0x4072d88c>
Post by Sebastien Bigaret
p = _
a.getPerson()
<Employee.Employee object at 0x4072d88c>
Post by Sebastien Bigaret
a.getPerson().getId()
1
Post by Sebastien Bigaret
a.isFault()
False
Post by Sebastien Bigaret
p.getAddress()
<Modeling.FaultHandler.AccessArrayFaultHandler instance at 0x40733a4c>
Post by Sebastien Bigaret
list(p.getAddress())
[<Address.Address object at 0x4071c74c>]
Post by Sebastien Bigaret
ec.fetch('Address', 'Person.id == 1')
[]
Post by Sebastien Bigaret
ec.fetch('Address', 'Person.id == 1', isDeep=1)
[]
Wooo, I didn't even realize how serious this was. The 'isDeep' flag
is unfortunately of no help here, because the problem is not with
Address and its underlying hierarchy (controlled by 'isDeep' flag) but
because of the generated SQL statement which obviously doesn't take
into account the sub-entities for entity Person:

SELECT DISTINCT t0.ID, t0.FK_PERSON
FROM ADDRESS t0, PERSON t1
WHERE ( PERSON.ID = 1 ) AND ( ADDRESS.FK_PERSON = PERSON.ID )

I've just submitted bug item #1017127 at
https://sourceforge.net/tracker/index.php?func=detail&aid=1017127&group_id=58935&atid=489335,
with your model and the corresponding code attached.

I'll search a definitive fix soon --my current thinking being about
the way that would allow even more complicated qualifiers to be
correctly handled as well, such as with: person.addresses.id in [2,3]
or w/ a qualifier traversing two or more relationships involving
entities with sub-entities.

Thanks a lot for the precise report,

-- Sébastien.
Sebastien Bigaret
2004-08-30 19:10:01 UTC
Permalink
I'm currently working on the issue, I thought I could share my current
thoughts.
Post by Sebastien Bigaret
SELECT DISTINCT t0.ID, t0.FK_PERSON
FROM ADDRESS t0, PERSON t1
WHERE ( PERSON.ID = 1 ) AND ( ADDRESS.FK_PERSON = PERSON.ID )
while it should take into account the class hierarchy, so that both
PERSON and EMPLOYEE are traversed.

I was initially thinking of making as many SQL queries as there are
pssible paths through the relationships (here, two: ADDRESS to PERSON,
and ADDRESS to EMPLOYEE) --but then I remembered these could SQL queries
could be UNIONed

So:
ec.fetch('Address', 'Person.id == 1')

should trigger the following query on the database [1]:

SELECT DISTINCT t0.ID, t0.FK_PERSON
FROM ADDRESS t0
INNER JOIN EMPLOYEE t1 ON t0.FK_PERSON=t1.ID
WHERE t1.ID = 1
UNION
SELECT DISTINCT t0.ID, t0.FK_PERSON
FROM ADDRESS t0
INNER JOIN PERSON t1 ON t0.FK_PERSON=t1.ID
WHERE t1.ID = 1


NB: Incidentally, that UNION thing could also be a solution for the
current limitation of sort orderings that cannot be used w/ the
isDeep flag being set! Another story, though


Now the question is: suppose I have two plus two paths in the same fetch
spec., such as in:

AND:
ec.fetch('Address', 'person.id in [1,2,3] AND person.x.y like...')

OR:
ec.fetch('Address', 'person.id in [1,2,3] OR person.x.y like...')

--> In the case of the 'AND' operator, do the two 'Person' refer to the
same table? In other words, do we get only two SELECTs union'ed?

In the case of the 'OR' operator, do the two 'Person' refer
independant tables, either the same or different ones? In other
words, do we get 2*2=4 SELECTs union'ed?
NB: 2==len([Person, Employee])

Is this what we want here, I fear I could miss something here? Any
comments will be appreciated!!

-- Sébastien.



[1] w/ INNER JOINs or the equivalent WHERE clause if inner joins aint
supported.
Post by Sebastien Bigaret
Post by John Lenton
On 29 Jul 2004 10:30:02 +0200, Sebastien Bigaret
Post by Sebastien Bigaret
Again, could you be a little more explicit, or exhibit a short
example? Sorry for getting back on this, but the only way I think this
can happen is the one I already posted (see below); if it's not the
case, this is a serious bug I definitely do not want to let escape.
sorry for the delay in answering, and the confusion when doing so. I
feel terribly embarrassed :(
Please don't feel embarrassed, that was worth waiting for it.
Post by John Lenton
HOWever, here goes.
With the attached model (which is ugly, and breaks some of your naming
conventions, but I just hacked out the attributes and renamed things
from the model being used by a team I'm helping to get up to speed),
Post by Sebastien Bigaret
from Modeling.EditingContext import EditingContext
import Store
ec=EditingContext()
ec.fetch('Address')[0]
<Address.Address object at 0x4071c74c>
Post by Sebastien Bigaret
a = _
p = ec.fetch('Person', isDeep=1)[0]
<Employee.Employee object at 0x4072d88c>
Post by Sebastien Bigaret
p = _
a.getPerson()
<Employee.Employee object at 0x4072d88c>
Post by Sebastien Bigaret
a.getPerson().getId()
1
Post by Sebastien Bigaret
a.isFault()
False
Post by Sebastien Bigaret
p.getAddress()
<Modeling.FaultHandler.AccessArrayFaultHandler instance at 0x40733a4c>
Post by Sebastien Bigaret
list(p.getAddress())
[<Address.Address object at 0x4071c74c>]
Post by Sebastien Bigaret
ec.fetch('Address', 'Person.id == 1')
[]
Post by Sebastien Bigaret
ec.fetch('Address', 'Person.id == 1', isDeep=1)
[]
Wooo, I didn't even realize how serious this was. The 'isDeep' flag
is unfortunately of no help here, because the problem is not with
Address and its underlying hierarchy (controlled by 'isDeep' flag) but
because of the generated SQL statement which obviously doesn't take
SELECT DISTINCT t0.ID, t0.FK_PERSON
FROM ADDRESS t0, PERSON t1
WHERE ( PERSON.ID = 1 ) AND ( ADDRESS.FK_PERSON = PERSON.ID )
I've just submitted bug item #1017127 at
https://sourceforge.net/tracker/index.php?func=detail&aid=1017127&group_id=58935&atid=489335,
with your model and the corresponding code attached.
I'll search a definitive fix soon --my current thinking being about
the way that would allow even more complicated qualifiers to be
correctly handled as well, such as with: person.addresses.id in [2,3]
or w/ a qualifier traversing two or more relationships involving
entities with sub-entities.
Thanks a lot for the precise report,
-- Sébastien.
John Lenton
2004-08-30 20:09:01 UTC
Permalink
On 30 Aug 2004 23:09:40 +0200, Sebastien Bigaret
Post by Sebastien Bigaret
I'm currently working on the issue, I thought I could share my current
thoughts.
Post by Sebastien Bigaret
SELECT DISTINCT t0.ID, t0.FK_PERSON
FROM ADDRESS t0, PERSON t1
WHERE ( PERSON.ID = 1 ) AND ( ADDRESS.FK_PERSON = PERSON.ID )
while it should take into account the class hierarchy, so that both
PERSON and EMPLOYEE are traversed.
I was initially thinking of making as many SQL queries as there are
pssible paths through the relationships (here, two: ADDRESS to PERSON,
and ADDRESS to EMPLOYEE) --but then I remembered these could SQL queries
could be UNIONed
ec.fetch('Address', 'Person.id == 1')
SELECT DISTINCT t0.ID, t0.FK_PERSON
FROM ADDRESS t0
INNER JOIN EMPLOYEE t1 ON t0.FK_PERSON=t1.ID
WHERE t1.ID = 1
UNION
SELECT DISTINCT t0.ID, t0.FK_PERSON
FROM ADDRESS t0
INNER JOIN PERSON t1 ON t0.FK_PERSON=t1.ID
WHERE t1.ID = 1
NB: Incidentally, that UNION thing could also be a solution for the
current limitation of sort orderings that cannot be used w/ the
isDeep flag being set! Another story, though
yes, I was going to mention that in the other thread, but then I saw
that it involved modifying code all the way down to the database
adapter, and thought you'd be against it. Glad to know you aren't; you
do realize this will mean you need a second query to get at attributes
not present in the base class? Or would you select with the union of
all attributes, providing dummy values for those not present in each
table?
Post by Sebastien Bigaret
Now the question is: suppose I have two plus two paths in the same fetch
ec.fetch('Address', 'person.id in [1,2,3] AND person.x.y like...')
ec.fetch('Address', 'person.id in [1,2,3] OR person.x.y like...')
--> In the case of the 'AND' operator, do the two 'Person' refer to the
same table? In other words, do we get only two SELECTs union'ed?
In the case of the 'OR' operator, do the two 'Person' refer
independant tables, either the same or different ones? In other
words, do we get 2*2=4 SELECTs union'ed?
if we were able to do sub-selects, we would (or could) write either as

SELECT * FROM (SELECT * FROM Person UNION SELECT ... FROM Employee
) AS Person WHERE <qualifier>

(because that is what we mean, right?) and this reduces to

SELECT * FROM Person WHERE <qualifier> UNION \
SELECT ... FROM Employee WHERE <qualifier>

irrespective of <qualifier>.
Post by Sebastien Bigaret
Is this what we want here, I fear I could miss something here? Any
comments will be appreciated!!
HTH---maybe it's me missing something, however; the "that is what we
mean" assumption is a strong one (in that it has big consquences).
--
John Lenton (***@gmail.com) -- Random fortune:
bash: fortune: command not found
John Lenton
2004-07-23 14:49:03 UTC
Permalink
On 23 Jul 2004 15:02:00 +0200, Sebastien Bigaret
Post by Sebastien Bigaret
Now you'll see that the discussion "ended" there with an open
question, about if and how this could/should be changed. Time to
rediscuss this I guess, huh?
ok, I've got no self-control, and I read the post. I then added the
following __getattr__ to CustomObject---I'm not at all certain this is
where it belongs, but it worked for me :) Not in production, and not
tested, but I thought I'd post it anyway

It does some magic with sys._getframe to ensure it's not called
recursively; it does it after checking the attribute doesn't start
with an underscore---which I'm not certain is ok.

def __getattr__(self, attr):
if attr.startswith('_'):
raise AttributeError
f = sys._getframe()
if f.f_code is f.f_back.f_code:
raise AttributeError
print >> sys.stderr, "*** faulting object %s: %s called" % (self, attr)
self.willRead()
return getattr(self, attr)

if this is the kind of thing we're talking about, I see no problem
with it---the default behaviour is preserved due to the raising of
AttributeErrors, and it's not *too* expensive. The _getframe magick
isn't essential, but it helps.

This still didn't fix the other issue, however...
--
John Lenton (***@gmail.com) -- Random fortune:
bash: fortune: command not found
Loading...