Problems with Inheritence

0 views
Skip to first unread message

Jeff Watkins

unread,
Oct 27, 2005, 8:48:32 AM10/27/05
to sqlobjec...@lists.sourceforge.net, turbo...@googlegroups.com
While working on the identity framework for TurboGears, I've been
trying to come up with a clean way to allow developers to provide
their own identity model classes. One thought was to load the name of
the developer's preferred User class and instantiate that instead of
the default User class.

So this means I have the following model:

class User(SQLObject):
class sqlmeta:
table="user_table"
# meaningful columns left out for brevity
groups=RelatedJoin( "Group", intermediateTable="user_group",
joinColumn="user_id", otherColumn="group_id" )

class Group(SQLObject):
class sqlmeta:
table="group_table"
# meaningful group info left out for brevity
users=RelatedJoin( "User", intermediateTable="user_group",
joinColumn="group_id", otherColumn="user_id" )

All works great so far. But then I add an override for the User class
to the config file to specify MyUser instead of User. MyUser is
defined as:

class MyUser(User):
# additional package specific stuff

However, when I instantiate a MyUser object and add it to a group,
attempting to retrieve the list of groups for that object results in
an Exception:

miles:~/Sites/turbo/cms> tg-admin shell
Python 2.4.1 (#2, Mar 31 2005, 00:05:10)
[GCC 3.3 20030304 (Apple Computer, Inc. build 1666)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> u= MyUser.get(1)
>>> u
<MyUser 1 userId='jeff' password='xxxx'
emailAddress='je...@metrocat.org' displayName='Jeff Watkins'
created='datetime.datetime...)' age=0>
>>> u.groups
Traceback (most recent call last):
File "<console>", line 1, in ?
File "<string>", line 1, in <lambda>
IndexError: list index out of range
>>>

Now I suppose this is caused by the join relating Users and Groups,
but I would have expected (wrongly, I guess) that a relation between
MyUser and Groups would automatically be created by inheriting the
RelatedJoin in User.

What am I missing here?

--
Jeff Watkins
http://metrocat.org/

'I know about people who talk about suffering for the common good.
It's never bloody them! When you hear a man shouting "Forward, brave
comrades!" you'll see he's the one behind the bloody big rock and the
one wearing the only really arrow-proof helmet!'
-- Rincewind gives a speech on politics. (Terry Pratchett,
Interesting Times)


parra...@gmail.com

unread,
Oct 27, 2005, 10:00:45 AM10/27/05
to TurboGears
Did you try to instantiate the User object ??


Parra

Leandro Lucarella

unread,
Oct 27, 2005, 11:45:50 AM10/27/05
to turbo...@googlegroups.com
Jeff Watkins, el jueves 27 de octubre a las 08:48 me escribiste:
>
> While working on the identity framework for TurboGears, I've been
> trying to come up with a clean way to allow developers to provide
> their own identity model classes. One thought was to load the name of
> the developer's preferred User class and instantiate that instead of
> the default User class.
>
> So this means I have the following model:
>
> class User(SQLObject):
> class sqlmeta:
> table="user_table"
> # meaningful columns left out for brevity
> groups=RelatedJoin( "Group", intermediateTable="user_group",
> joinColumn="user_id", otherColumn="group_id" )
>
> class Group(SQLObject):
> class sqlmeta:
> table="group_table"
> # meaningful group info left out for brevity
> users=RelatedJoin( "User", intermediateTable="user_group",
> joinColumn="group_id", otherColumn="user_id" )
>
> All works great so far. But then I add an override for the User class
> to the config file to specify MyUser instead of User. MyUser is
> defined as:
>
> class MyUser(User):
> # additional package specific stuff

Maybe you have to use InheritableSQLObject?

class User(InheritableSQLObject):
class sqlmeta:
table="user_table"
# meaningful columns left out for brevity
groups=RelatedJoin( "Group", intermediateTable="user_group",
joinColumn="user_id", otherColumn="group_id" )

class MyUser(User):
_inheritable = False
# additional package specific stuff

Reference:
http://www.sqlobject.org/Inheritance.html

--
LUCA - Leandro Lucarella - JID: luca(en)lugmen.org.ar - Debian GNU/Linux
.------------------------------------------------------------------------,
\ GPG: 5F5A8D05 // F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05 /
'--------------------------------------------------------------------'
Sometimes I think the sure sign that life exists elsewhere in the universe
Is that that none of them tried to contact us

Matthew Bevan

unread,
Oct 27, 2005, 1:39:24 PM10/27/05
to TurboGears
Howdy!

> While working on the identity framework for TurboGears, I've been
> trying to come up with a clean way to allow developers to provide
> their own identity model classes. One thought was to load the name of
> the developer's preferred User class and instantiate that instead of
> the default User class.

I discovered a while back the perfect way to do this. Have a global in
your Inheritance module called _User or whatever, and provide a
function or class method to set this variable. Then I should be able
to do something like:

inheritance.userClass(MyUserClass)
inheritance.groupClass(MyGroupClass)
inheritance.roleClass(MyRoleClass)

Internally, you create new instances (or manage instances of) _User,
_Group, and _Role, or whatever you happen to use.

> All works great so far. But then I add an override for the User class
> to the config file to specify MyUser instead of User. MyUser is
> defined as:
>
> class MyUser(User):
> # additional package specific stuff

You simply can not do that with SQLObject classes unless you specify
the top-most one as being an InheritableSQLObject. Even then, this
isn't a very elegant or efficient way of organizing the data, as it
creates two tables (one for the base class, one for MyUser) and
performs two lookups for each .get(). Simple overriding of a "pointer"
to a class would probably be best. IMHO. YMMV. &c. ;)

David Stanek

unread,
Oct 27, 2005, 5:44:22 PM10/27/05
to turbo...@googlegroups.com
Is there any documentation describing how the identity framework works? Or maybe a plan for the vision? I have only been able to piece together bits while trying to keep up with this list.

In my ideal world this would be a very simple framework:
  * Somehow tell TG that this page is protected (probably in the *.cfg files)
  * Somehow tell TG what module exports the identity interfaces (probably in the *.cfg files)
  * Define a generic interface or set of interfaces that are hooked where authentication is needed
  * Provide a sane out of the box implementation of the interfaces

This way very custom applications can just implement the interfaces and through the magic of duck typing everything just works. For instance, the traditional User object can be the sane out of the box implementation. But say you don't use a database for authentication...that is easy as a new implementation of the interface(s).

--
David Stanek
www.roninds.net

Jeff Watkins

unread,
Oct 27, 2005, 8:45:32 PM10/27/05
to turbo...@googlegroups.com

On 27 Oct, 2005, at 1:39 pm, Matthew Bevan wrote:
> I discovered a while back the perfect way to do this. Have a
> global in
> your Inheritance module called _User or whatever, and provide a
> function or class method to set this variable. Then I should be able
> to do something like:
>
> inheritance.userClass(MyUserClass)
> inheritance.groupClass(MyGroupClass)
> inheritance.roleClass(MyRoleClass)
>
> Internally, you create new instances (or manage instances of) _User,
> _Group, and _Role, or whatever you happen to use.

Actually, that's exactly what got me into all this mess. I read the
class out of the config file and instantiate one when the time comes.
The problem is that SQLObject isn't creating the necessary joins or
something. So when I ask the object derived from identity.model.User
for the Groups to which it belongs, I get an exception.

> You simply can not do that with SQLObject classes unless you specify
> the top-most one as being an InheritableSQLObject. Even then, this
> isn't a very elegant or efficient way of organizing the data, as it
> creates two tables (one for the base class, one for MyUser) and
> performs two lookups for each .get(). Simple overriding of a
> "pointer"
> to a class would probably be best. IMHO. YMMV. &c. ;)

I'm not certain I want to require you to use any of the columns in
the identity.model.User class. It should be sufficient to expose the
following properties: userId, groups, and permissions.

The Group objects returned by the groups property need only expose a
groupId property, and the Permission objects returned by the
permissions property only need to expose a permissionId property.

My hope was to allow developers to override only those classes they
*needed* to override. How much information do you usually store in a
group anyway. So most likely, only the User class needed to be
overridden.

I still think this is the ideal way to do this, but for expedience,
I'm going to have to think of alternatives.


--
Jeff Watkins
http://metrocat.org/

Getting an education was a bit like a communicable sexual disease. It
made you unsuitable for a lot of jobs and then you had the urge to
pass it on.
-- (Terry Pratchett, Hogfather)


Kevin Dangoor

unread,
Oct 27, 2005, 10:02:07 PM10/27/05
to turbo...@googlegroups.com
On 10/27/05, David Stanek <ron...@gmail.com> wrote:
> Is there any documentation describing how the identity framework works? Or
> maybe a plan for the vision? I have only been able to piece together bits
> while trying to keep up with this list.

There's not much yet. Nothing formal.

> In my ideal world this would be a very simple framework:
> * Somehow tell TG that this page is protected (probably in the *.cfg
> files)

There are actually a few ways to configure that a page is protected: a
decorator, a superclass and a filter (that may or may not currently
use cfg variables... but that would be the place to do it).

> * Somehow tell TG what module exports the identity interfaces (probably
> in the *.cfg files)

I don't know that this is defined yet.

> * Define a generic interface or set of interfaces that are hooked where
> authentication is needed

This is the idea.

> * Provide a sane out of the box implementation of the interfaces

Yep.

> This way very custom applications can just implement the interfaces and
> through the magic of duck typing everything just works. For instance, the
> traditional User object can be the sane out of the box implementation. But
> say you don't use a database for authentication...that is easy as a new
> implementation of the interface(s).

Exactly. What you've written is indeed the plan. It's not 100% there
yet, and I have yet to really tinker with Jeff's work. But, what
you've described is what will appear in 0.9.

Kevin

Jeff Watkins

unread,
Oct 27, 2005, 11:14:01 PM10/27/05
to turbo...@googlegroups.com

On 27 Oct, 2005, at 5:44 pm, David Stanek wrote:

> Is there any documentation describing how the identity framework
> works? Or maybe a plan for the vision? I have only been able to
> piece together bits while trying to keep up with this list.

David, I apologise for falling behind on the documentation. I posted
a quick how to on my web site (http://metrocat.org/nerd/2005/10/
identity-management-for-turbogears) and will be posting more in the
coming days. (I just finished a nifty diagram in OmniGraffle. What a
*really* cool app.)

> In my ideal world this would be a very simple framework:
> * Somehow tell TG that this page is protected (probably in the
> *.cfg files)

At the moment you can control access to an entire class or a single
method. Access control is based on either group membership or
permissions. And with the IP matching code from CatWalk, you'll be
able to control access based on originating IP address.

> * Somehow tell TG what module exports the identity interfaces
> (probably in the *.cfg files)

This is currently causing me a great deal of difficulty at the
moment. At the moment, the Identity code only requires a User class
that exposes a byUserId method which accepts a string and returns an
instance.

The User instance may have a property, groups, containing Group
objects with a groupId property.

The User instance may also have a property, permissions, containing
Permission objects with a permissionId property.

These User, Group and Permission objects to not need to be the same
as that provided by turbogears.identity.model.

My goal is to allow you to specify an alternate model module which
can provide classes which implement this "interface".

> * Define a generic interface or set of interfaces that are hooked
> where authentication is needed

I'm fairly certain the function decorators and SecureResource mixin-
class provide this functionality.

> * Provide a sane out of the box implementation of the interfaces

I like to think so.


--
Jeff Watkins
http://metrocat.org/

"Just because you have the right to do something, doesn't mean it's
the right thing to do."
-- Fred Friendly, former president of CBS News

Reply all
Reply to author
Forward
0 new messages