On Jul 21, 2011, at 10:48 AM, Michael Bayer wrote:

> 
> On Jul 21, 2011, at 2:11 AM, Fayaz Yusuf Khan wrote:
> 
>> Hi,
>> I have this declarative table model:
>> http://paste.pound-python.org/show/9857/
> 
> The short answer is you can't do it that way right now.   The "user" and 
> "timestamp" columns of C can't get assigned to the Table by declarative 
> because they've already been instrumented by the mapping of the Timeline 
> class - the columns generated on the TMixin class are replaced by the 
> instrumented versions of Timeline.
> 
> You'd have to forego the usage of mixins here or have C() use a mixin that 
> isn't in the hierarchy of Timeline.
> 
> Ticket #2226 is added to see if this can be fixed.

OK it's an invalid, though I made the error message with the 
ForeignKeyConstraint nicer in the latest tip.

When C inherits from CMixin and Timeline, Python's method resolution is going 
to put TMixin after Timeline, that is:

(<class '__main__.C'>, <class '__main__.CMixin'>, <class '__main__.Timeline'>, 
<class '__main__.TMixin'>, <class 'sqlalchemy.ext.declarative.Base'>, <class 
'__main__.TableName'>, <type 'object'>)

The "user" and "timestamp" columns you're looking to be copied separately to 
both C and Timeline only get applied to Timeline - since Timeline is mapped and 
it ends the scope of TMixin propagating its attributes outward.

A similar example shows how this behavior produces what's expected.  Below, 
Child inherits from Parent, but we would expect Child to get the behavior of 
y() from Parent, not that of SomeMixin which is "behind" Parent:

from sqlalchemy import Column, String, ForeignKey, Integer
from sqlalchemy.ext.declarative import declarative_base, declared_attr

class TableName(object):
   @declared_attr
   def __tablename__(cls):
       return cls.__name__

Base=declarative_base(cls=TableName)

class SomeMixin(object):
    @declared_attr
    def x(cls):
        return Column(String)

    @declared_attr
    def y(cls):
        return Column(String)

class Parent(SomeMixin, Base):
   id = Column(Integer, primary_key=True)

   def y(self):
       return "hi"

class SomeOtherMixin(SomeMixin):
    pass

# this:
class Child(SomeOtherMixin, Parent):
   id = Column(Integer, ForeignKey('Parent.id'), primary_key=True)

# is essentially the same here as:
#class Child(Parent):
#   id = Column(Integer, ForeignKey('Parent.id'), primary_key=True)

# Parent.y overrides the @declared_attr in SomeMixin
assert Child().y() == 'hi'

# so similarly, SomeMixin.x only applies to Parent
assert 'x' not in Child.__table__.c

# Child.x and Parent.x are the same mapped property,
# ultimately.
assert Child.x.property is Parent.x.property

In your example, the "user" and "timeline" columns as associated with C are not 
really "the same" as the columns on Timeline - the Timeline columns are the 
ultimate autogenerating PK columns, whereas those of "C" are referencing - they 
have a different role.   So it makes sense they be declared distinctly on 
CMixin.  CMixin extends TMixin below but it might as well not, as Timeline in 
the middle takes the place of TMixin.


from sqlalchemy import Column, String, ForeignKeyConstraint, Integer
from sqlalchemy.ext.declarative import declarative_base, declared_attr

class TableName(object):
   @declared_attr
   def __tablename__(cls):
       return cls.__name__

Base=declarative_base(cls=TableName)

class TMixin(object):

   @declared_attr
   def user(cls):
       return Column(String, primary_key=True)

   @declared_attr
   def timestamp(cls):
       return Column(Integer, autoincrement=False, primary_key=True)


class Timeline(TMixin, Base):

   snaptype = Column(Integer, primary_key=True, autoincrement=False)

   __mapper_args__ = {'polymorphic_on': snaptype}


class CMixin(TMixin):
   @declared_attr
   def user(cls):
       return Column(String, primary_key=True)

   @declared_attr
   def timestamp(cls):
       return Column(Integer, autoincrement=False, primary_key=True)

   @declared_attr
   def __table_args__(cls):
       return (ForeignKeyConstraint(
                   ['user', 'timestamp'],
                   ['Timeline.user', 'Timeline.timestamp']),{})

   @declared_attr
   def __mapper_args__(cls):
       return {'polymorphic_identity': CMixin.__subclasses__().index(cls)}

class C(CMixin, Timeline):
   pass






> 
> 
> 
>> ==========================================================
>> from sqlalchemy import Column, String, ForeignKeyConstraint, Integer
>> from sqlalchemy.ext.declarative import declarative_base, declared_attr
>> 
>> 
>> class TableName(object):
>>   @declared_attr
>>   def __tablename__(cls):
>>       return cls.__name__
>> 
>> 
>> Base=declarative_base(cls=TableName)
>> 
>> 
>> class TMixin(object):
>> 
>>   @declared_attr
>>   def user(cls):
>>       return Column(String, primary_key=True)
>> 
>>   @declared_attr
>>   def timestamp(cls):
>>       return Column(Integer, autoincrement=False, primary_key=True)
>> 
>> 
>> class Timeline(TMixin, Base):
>> 
>>   snaptype = Column(Integer, primary_key=True, autoincrement=False)
>> 
>>   __mapper_args__ = {'polymorphic_on': snaptype}
>> 
>> 
>> class CMixin(TMixin):
>> 
>>   @declared_attr
>>   def __table_args__(cls):
>>       return (ForeignKeyConstraint(
>>                   ['user', 'timestamp'],
>>                   ['Timeline.user', 'Timeline.timestamp']),)
>> 
>>   @declared_attr
>>   def __mapper_args__(cls):
>>       return {'polymorphic_identity': CMixin.__subclasses__().index(cls)}
>> 
>> 
>> class C(CMixin, Timeline):
>> 
>>   pass
>> 
>> ==========================================================
>> 
>> But on running this code, I get:
>> ==========================================================
>> Traceback (most recent call last):
>> File "schema.py", line 45, in <module>
>>   class C(CMixin, Timeline):
>> File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/ext/declarative.py", 
>> line 1129, in __init__
>>   _as_declarative(cls, classname, cls.__dict__)
>> File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/ext/declarative.py", 
>> line 1027, in _as_declarative
>>   **table_kw)
>> File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/schema.py", line 
>> 265, in __new__
>>   table._init(name, metadata, *args, **kw)
>> File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/schema.py", line 
>> 340, in _init
>>   self._init_items(*args)
>> File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/schema.py", line 64, 
>> in _init_items
>>   item._set_parent_with_dispatch(self)
>> File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/events.py", line 
>> 227, in _set_parent_with_dispatch
>>   self._set_parent(parent) 
>> File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/schema.py", line 
>> 2010, in _set_parent
>>   col = table.c[col]
>> File "/usr/local/lib/python2.7/dist-
>> packages/sqlalchemy/util/_collections.py", line 88, in __getitem__
>>   return self._data[key]
>> KeyError: 'user'
>> ==========================================================
>> 
>> I don't get any error if C isn't inheriting from Timeline (ie, no 
>> joint-table 
>> inheritance). What could be wrong here?
>> 
>> sqlalchemy.__version__ = '0.7.1'
>> 
>> And thanks in advance.
>> 
>> -- 
>> Fayaz Yusuf Khan
>> Cloud developer and designer
>> Dexetra SS, Kochi, India
>> fayaz.yusuf.khan_AT_gmail_DOT_com
>> fayaz_AT_dexetra_DOT_com
>> +91-9746-830-823
>> <schema.py>
> 
> -- 
> 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 
> sqlalchemy+unsubscr...@googlegroups.com.
> For more options, visit this group at 
> http://groups.google.com/group/sqlalchemy?hl=en.
> 

-- 
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 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to