On Thursday, July 21, 2011 09:27:21 PM Michael Bayer wrote: > 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. Well, I'd really like to see this feature implemented. > > > > 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'>) Maybe, joint-table inheritance and mixin syntaxes should be made more distinct and unambiguous? It may well be impossible (and backward incompatible) to implement in the current code-base, but a usage recipe which implements this behaviour might be possible. > > 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.
signature.asc
Description: This is a digitally signed message part.