Reputation: 20315
I have the following setup:
class Attribute(object):
a = Column(Integer)
class Thing(Base, Attribute):
b = Column(Integer)
class Subthing(Thing):
c = COlumn(Integer)
However, both Thing and Subthing will have the Attribute mixin, meaning they'll both have the columns specified in Attribute:
Thing: a | b
Subthing: a | c
I only want these mixin columns present in Thing and not Subthing:
Thing: a | b
Subthing: c
Is this possible or will I have to resort to making columns and methods manually instead of using a mixin for every Thing?
Upvotes: 0
Views: 355
Reputation: 75117
This is a behavioral inconsistency that's developed as the result from the behavior of @declared_attr slowly deviating from a behavioral contract that was only tested against the "column copy" use case. The original use case of the "column on mixin" was to apply it to all inheriting classes equally, however @declared_attr, which was developed a bit later, didn't adopt this behavior, hence inconsistent.
Changing "column copy" to only take effect for the non-subclass is a backwards-incompatible behavioral change, so is in the upcoming 0.8 only. This is ticket #2565 (http://www.sqlalchemy.org/trac/ticket/2565) and is resolved in r9baa197f2c67.
test:
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base, declared_attr
Base = declarative_base()
class Mixin(object):
a = Column(Integer)
@declared_attr
def b(cls):
return Column(Integer)
class A(Mixin, Base):
__tablename__ = 'a'
id = Column(Integer, primary_key=True)
class B(A):
__tablename__ = 'b'
id = Column(Integer, ForeignKey('a.id'), primary_key=True)
assert 'a' in A.__table__.c
assert 'b' in A.__table__.c
assert 'a' not in B.__table__.c
assert 'b' not in B.__table__.c
Upvotes: 1
Reputation: 2826
I just ran into the same issue. It turns out if you define columns in your mixin class using @declared_attr
, SQLAlchemy behaves properly. Columns declared directly in the mixin leak into sub-classes when using joined-table inheritance.
Upvotes: 0
Reputation: 23331
Subthing
gets a
from the parent by definition of inheritance. If you don't want that behavior, then Subthing
cannot inherit from that object. A possible solution is to introduce yet another base class.
class Attribute(object):
@declared_attr # the actual syntax on SQLAlchemy for doing mixins
def a(cls):
return Column(Integer)
class BaseThing(Base):
pass
class Thing(BaseThing, Attribute):
b = Column(Integer)
class Subthing(BaseThing):
c = Column(Integer)
Then Subthing only has column c
, and Thing has a
, and b
.
Upvotes: 4