> you could just be using one mapper for all the classes here.  its
> almost like you should monkeypatch class_mapper() and object_mapper()
> and just be done with it.
>
> of course the reason mappers are usually specific to a class is
> because, every class would have completely different attributes and
> relations.  but it seems here that is not the case.
>

Well, the subclasses actually map to a select on the table while Thing
maps to the whole table.  So the Class mapped over different sets even
though they had identical attributes and relations.  But the primary
reason for the additional classes is to get additional methods/
functionality so I may be able to drop the added complexity of mapping
to selects.  How do you reuse a mapper on additional classes?

class Foo(object):
  pass

foo_mapper = mapper(...)

class Bar(Foo):
  pass

then what?
foo_mapper.addClass(?) or something ??


> so, you want people to say:
>
> s = Server(model='apple')
>
> then later, they ...upgrade ?  by v0.1 -> v0.2 you mean a new copy of
> your framework ?  or just hypothetical versions of the user's
> application ?
>
> then they say:
>
> s = some_query.get_my_thing(criterion)
>
> and they get back an AppleServer, which is some kind of improvement
> over Server.


I guess I should explain further what I'm trying to build.  My app is
a tool to help with managing clusters.  This includes everything from
Datacenters, Racks, Servers, Switches, etc.  I don't know ahead of
time the sorts of specific Things and functionality that might be
useful so I'm trying to make things as flexible, generic, and easy to
extend as possible.  So at the core I suppose you could call this
Thing/Attributes abstraction the framework upon which I and others
would code "drivers" like Server, SunServer, AppleServer, Switch,
CiscoSwitch, CiscoRouter, Pool, Location, etc etc.  All those together
with a command line interface to facilitate scripting is an app called
clusto.  So, I'm a sysadmin and I'm using this tool.  I just bought a
Load Balancer 5000.  I immediately put it into my system as a plain
old LoadBalancer(manufacturer='Load Balancer', model='5000').  Later
on I decide that I'd like clusto to be able to add servers to my fancy
LoadBalancer5000 configuration.  Nobody else has implemented the
functionality yet, so in true open source form, I dive in and do it
myself:

class LoadBalancer5000(LoadBalancer):
  meta_attrs = [('manufacturer',  'Load Balancer'), ('model', '5000')]

  def addServer(self, someserver):
     # magic

done.  No futzing with the database, no diving into obscure parts of
the code, nothing.  I just plop that class into the right path and it
works.  With a clever command line and scripting interface it may even
be useful.


>
> would they ever get a Server back again ?  if not, why does the
> database need to change ?  why not just map AppleServer to
> "server" ?

Because there might also be a SunServer and a FooBarServer and an
AlphaServer.

> also, arent you concerned about query overhead here ?
> with all your objects being completely homogenized into a vertical
> structure and all, that is.

Yeah, this tool isn't built for speed or high load.  It's built for
flexibility and usefulness.  If I want speed I'll figure out caching
and optimization later.  Also, it is version 0.0001 so it's acting, in
part, as a proof of concept.

> theres no straightforward way for me to
> get a list of all the AppleServers, for example, since id have to
> query all these different attributes just to identify those objects.
>

So, underneath the hood, to get all the AppleServers you'd do:

## pseudocode
for attr in SomeThingClass.all_meta_attrs:
 # all_meta_attrs is a list of all the meta_attrs for that class going
up the inheritance chain, cls.mro()
 thingquery += and_(Attribute.c.key==attr[0],
Attribute.c.value==attr[1])

select(and_(Thing.c.name==Attribute.c.name, thingquery))
#that should get all the Thing that can be managed by the given
class.  Maybe not "straightforward" but not terribly complex either.

So, in my implementation, the metaclass mapped each Class to such a
select.  I am mapping against different selectables, and so having
different mappers made sense.  So if I did SA functions like:

AppleServer.select(and_(Attribute.c.key='numports',
Attribute.c.value='2'))

I'd only get AppleServers with ('numports', '2') and not any other
types of Things.  At one point I got things working as I just
described, but I'm not sure if that was the case in my latest
iteration of the code.

>
>
> > I'm still soaking in these examples.  I think what I really want is to
> > have mapper accept something like polymorphic_func and base_class.  So
> > I would pass it my _setProperClass function and Thing.  The mapper
> > will build against Thing and then run _setProperClass against the
> > instance.  Yeah, I'm cheating, cause that's kind of basically what I'm
> > doing now.  I'm just not sure how else to achieve the functionality
> > I'm looking for.
>
> ah well making polymorphic_on optionally a callable i can see..i
> wonder if someone has suggested that already.   thats a good way to
> provide this hook for you in just the right place (oh you know what,
> i think hibernate allows something like this).  not sure what you
> mean by "base_class" unless you just mean the existing "inherits"
> argument to mapper().

base_class is probably not necessary.  Just stuck it in there in case
the mapper needed to know the base class of the all the other
Classes.

> however, didnt you say that your class
> attributes come from a different table ?  in that case this is still
> not going to work...if youre relying upon eager loading of related ,
> multiple sets of rows, thats not available until well after the
> polymorphic decisions have been made.  the most that polymorhpic_func
> could get is the first row with the Thing's primary key in it.
>

That's a good point.  I suppose the function could use that primary
key to select stuff out of the Attributes table and then analyze those
to determine the proper class.  But that seems like an unhappy hack.
Why isn't there a hook into a post-populate part of the mapping?  Or
whatever the absolute very last step of making an instance happens to
be.  Does such a thing exist and I just missed it?

> As a temporary detour, I was curious if this little hack does it too
> - change your metaclass to not generate any new mappers.  Just have
> one Thing mapper, and instead of the metaclass making a new mapper
> when it sees Server (and all the other classes), do this:
>
> from sqlalchemy.orm import mapperlib
>
> thing_mapper = class_mapper(Thing)
> mapperlib.mapper_registry[mapperlib.ClassKey(Server, None)] =
> thing_mapper
>
> that will just make Server be linked to the same mapper as that of
> Thing.  combine that with your populate_instance() extension as
> usual.   just wondering if that works all the way through.  this
> wouldnt use any SA inheritance/polymorphism or anything like that.

Ok, so that actually largely fixes the error.  I have a couple other
errors I need to work out.  Probably related to sessions.

What exactly are those lines doing?  getting the mapper of the Thing
class, and assigning the same mapper to the Server class?  It just hit
me that it's probably the answer to the question I asked towards the
top of the post.

One thing I lose by doing it this is having Server mapped to only its
subset of Things in the db.  I was using assign_mapper to accomplish
two different goals.  One, to populate instances of Things with their
attribute and row values.  Two, to add useful member functions
like .select() to the class so that I could more cleanly work on the
subset managed by the class.  I suppose I could write my own functions
to provide the latter and that might actually be the more correct way
to expose such functionality.

I think this got me most of the way towards fixing my code.

I still like the idea of the polymorphic_on callable, but I didn't try
the patch.

Thanks,
-Ron


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to