On Oct 13, 2008, at 12:22 PM, [EMAIL PROTECTED] wrote:

>
>> What is preventing you from simply mapping different classes ?
> Different Customers uses different (her own) table spaces. So we  
> must map a Class to different tables.
>
> You remove entity_name, but how you specify the 'user dependent  
> storage' in sqlalchem 0.5? This behavior is a typical orm-property.  
> I know its a "enterprise requirement". But it is the reason why I  
> use sqlalchemy.

It's specifically a Hibernate/Java ORM requirement, because in Java,  
subclasses are not cheap.  The reality is, by using "entity_name", you  
associate an "entity_name" attribute with your instance after it's  
added to the Session, which then determines behavior of that  
instance.  Before its added, there is no "entity_name", and behavior  
is undefined.  In Hibernate, the ORM defines much less behavior on  
classes since you are required to spell out all instance members and  
collections explicitly.   Hibernate also does not place any behavior  
upon non-instantiated classes; most Python ORMs like SQLAlchemy do,  
i.e. you can say "Customer.name=='ed'".  0.5 relies much more heavily  
on class behavior in terms of querying than previous versions.  As you  
can see here:  
http://www.hibernate.org/hib_docs/v3/reference/en/html/persistent-classes-dynamicmodels.html
 
  , Hibernate uses entity_name to add "dynamic" class behavior to  
Java, something the language otherwise does not have.  Python is  
already a dynamic language so there's many ways to solve the problem  
that entity_name does in Hibernate.

Because of the way SQLA adds behavior to instances, through  
instrumentation, the only way "entity_name" can ever work in a solid,  
"enterprise" sense of the word is if it is specified upon instance  
creation, i.e. MyInstance(foo, bar, entity_name='foo').   Class  
behavior is also not possible at all and special "wrappers" are needed  
to achieve SQLA's class level behavior, i.e. SomeClass =  
myentity(MyClass, 'foo'); SomeClass.name == 'ed' (additionally, any  
number of classes can be sent to session.query() now, so theres no  
place for an "entity_name" keyword argument in any case).   There is  
really no "abstraction" of entity from mapped instance here - they  
need to be stated together.

With that restriction in mind, you can get similar behavior without an  
entity_name feature at all:

        class Customer(object):
                def __new__(cls, entity_name, *args, **kw):
                        if entity_name == 'ed':
                                return object.__new__(EdCustomer)
                        elif entity_name == 'wendy':
                                return object.__new__(WendyCustomer)
                        else:
                                raise Exception("unknown entity %s" % 
entity_name)

querying looks like:

        sess.query(WendyCustomer).filter(WendyCustomer.some_number==12)

an equivent function to mapper() with entity_name which creates types  
dynamically (and also stores them in a dict):

        mappers = {}
        def entity_mapper(basecls, table, entity_name, **kwargs):
                cls = type.__new__(type, "%s%s" % (entity_name, 
basecls.__name__),  
(basecls,), {})
                m = mapper(cls, table, **kwargs)
                mappers[(basecls, entity_name)] = m
                return cls

If you've built entity_mapper, we can rewrite Customer to use the  
mappers as a guide:

        def entity_named(cls, entity_name):
                try:
                        return mappers[(cls, entity_name)].class_
                except KeyError:
                        raise Exception("unknown entity %s" % entity_name)

        class Customer(object):
                def __new__(cls, entity_name, *args, **kw):
                        return object.__new__(entity_named(cls, entity_name))

So the equivalent of:

        c = Customer()
        sess.save(c, entity_name='foo')

becomes:

        c = Customer(entity_name='foo')
        sess.save(c)

the equivalent of:

        sess.query(Customer,  
entity_name="foo").filter(customer_table.c.name=='ed')....

becomes:

        EdCustomer = entity_named(Customer, "foo")
        sess.query(EdCustomer).filter(EdCustomer.name=='ed')...

Theres many ways to roll behavior like the above, these are just some  
ideas.    The main idea is that entity_name isn't adding any value  
that you can't create more effectively on your own.

For more background, I wrote about this here:  
http://groups.google.com/group/sqlalchemy/browse_thread/thread/9e23a0641a88b96d/391326fcf89c05d4?lnk=gst&q=entity_name#391326fcf89c05d4
 
  .

As far as the specfic issue you're having, it does seem like an 0.4  
bug - its consulting the lazy loader for the instance when none should  
be consulted at all, if the object is only transient.  So Jek's  
suggestion of placing the object in the session before accessing the  
collection is a good bet for 0.4.



--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
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