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.