Mike: Actually, the classproperty I was using was a custom decorator that I made. It doesn't work quite the same as the example you provided, so I will have to compare the two approaches.
Here is the one I had: class classproperty(property): """ This defines a decorator that can be used to describe a read-only property that is attached to the class itself instead of an instance. """ def __get__(self, cls, owner): return classmethod(self.fget).__get__(None, owner)() I will try your method and see how it works for my project. Thanks for the response! On Mon, Jan 2, 2017 at 9:22 AM, mike bayer <mike...@zzzcomputing.com> wrote: > the usage of "classproperty" here suggests you're using SQLAlchemy's > internal function, which has special meaning in that it gets treated like > declared_attr, and you can see in INSERT it is using HybridModel for > everyone. Using a different classproperty approach below the test case > passes. > > from sqlalchemy import * > from sqlalchemy.orm import * > from sqlalchemy.ext.declarative import declarative_base, declared_attr > from sqlalchemy.ext.hybrid import hybrid_property, Comparator > > Base = declarative_base() > > class classproperty(property): > """A decorator that behaves like @property except that operates > on classes rather than instances. > > The decorator is currently special when using the declarative > module, but note that the > :class:`~.sqlalchemy.ext.declarative.declared_attr` > decorator should be used for this purpose with declarative. > > """ > > def __init__(self, fget, *arg, **kw): > super(classproperty, self).__init__(fget, *arg, **kw) > self.__doc__ = fget.__doc__ > > def __get__(desc, self, cls): > return desc.fget(cls) > > > class ModelBase(Base): > __abstract__ = True > > def __repr__(self): > return "{} ({}:{})".format(type(self).__name__, self.name, self.id > ) > > > class HybridType(ModelBase): > __tablename__ = 'hybrid_types' > id = Column(Integer, primary_key=True) > name = Column(Text) > > > class HybridModel(ModelBase): > __tablename__ = 'hybrids' > > id = Column(Integer, primary_key=True) > name = Column(Text) > hybrid_type_id = Column(Integer, ForeignKey('hybrid_types.id'), > nullable=False) > hybrid_type = relationship('HybridType') > > def __init__(self, *args, **kwargs): > self.hybrid_type_name = self.hybrid_type_identity > return super().__init__(*args, **kwargs) > > @classproperty > def hybrid_type_identity(cls): > return cls.__name__ > > @declared_attr > def __mapper_args__(cls): > return dict( > polymorphic_on=cls.hybrid_type_name_subquery(), > polymorphic_identity=cls.hybrid_type_identity, > ) > > @hybrid_property > def hybrid_type_name(self): > return self.hybrid_type.name > > @hybrid_type_name.setter > def hybrid_type_name(self, value): > self.hybrid_type_id = ( > select([HybridType.id]). > where(HybridType.name == value) > ) > > @hybrid_type_name.expression > def hybrid_type_name(cls): > return cls.hybrid_type_name_subquery() > > @classmethod > def hybrid_type_name_subquery(cls): > return select([HybridType.name]).where(HybridType.id == > cls.hybrid_type_id).as_scalar() > > class HybridComparator(Comparator): > > def operate(self, op, other): > return op(HybridType.hybrid_type_id, > select([HybridType.id]).where(HybridType.name == other).as_scalar()) > > @hybrid_type_name.comparator > def hybrid_type_name(cls): > return cls.HybridComparator(cls) > > > class HybridAlpha(HybridModel): > pass > > > class Hybrita(HybridModel): > pass > > > e = create_engine("sqlite://", echo=True) > Base.metadata.create_all(e) > session = Session(e) > > > session.add(HybridType(name=HybridAlpha.hybrid_type_identity)) > session.add(HybridType(name=Hybrita.hybrid_type_identity)) > session.add(HybridAlpha(name='alpha_instance')) > session.add(Hybrita(name='beta_instance')) > > > all_alphas = session.query(HybridAlpha).all() > assert session.query(HybridModel).count() == 2 > print("------") > assert session.query(HybridAlpha).count() == 1 > assert session.query(Hybrita).count() == 1 > > > > > On 12/29/2016 11:09 AM, Tucker Beck wrote: > >> Mike, >> >> Here's the solution I came up with. It shows the 'inverted select' used >> in the comparator vs the hybrid expression >> >> class ModelBase(db.Model): >> __abstract__ = True >> >> def __repr__(self): >> return "{} ({}:{})".format(type(self).__name__, self.name >> <http://self.name>, self.id <http://self.id>) >> >> >> class HybridType(ModelBase): >> __tablename__ = 'hybrid_types' >> id = db.Column(db.Integer, primary_key=True) >> name = db.Column(db.Text) >> >> >> class HybridModel(ModelBase): >> __tablename__ = 'hybrids' >> >> id = db.Column(db.BigInteger, primary_key=True) >> name = db.Column(db.Text) >> hybrid_type_id = db.Column(db.Integer, >> db.ForeignKey('hybrid_types.id <http://hybrid_types.id>'), >> nullable=False) >> hybrid_type = db.relationship('HybridType') >> >> def __init__(self, *args, **kwargs): >> self.hybrid_type_name = self.hybrid_type_identity >> return super().__init__(*args, **kwargs) >> >> @classproperty >> def hybrid_type_identity(cls): >> return inflection.underscore(cls.__name__) >> >> @declared_attr >> def __mapper_args__(cls): >> return dict( >> polymorphic_on=cls.hybrid_type_name_subquery(), >> polymorphic_identity=cls.hybrid_type_identity, >> ) >> >> @hybrid_property >> def hybrid_type_name(self): >> return self.hybrid_type.name <http://self.hybrid_type.name> >> >> @hybrid_type_name.setter >> def hybrid_type_name(self, value): >> self.hybrid_type_id = ( >> select([HybridType.id]). >> where(HybridType.name == value) >> ) >> >> @hybrid_type_name.expression >> def hybrid_type_name(cls): >> return cls.hybrid_type_name_subquery() >> >> @classmethod >> def hybrid_type_name_subquery(cls): >> return select([HybridType.name]).where(HybridType.id == >> cls.hybrid_type_id).as_scalar() >> >> >> class HybridComparator(Comparator): >> >> def operate(self, op, other): >> return op(HybridType.hybrid_type_id, >> select([HybridType.id]).where(HybridType.name == other).as_scalar()) >> >> >> @hybrid_type_name.comparator >> def hybrid_type_name(cls): >> return cls.HybridComparator(cls) >> >> >> class HybridAlpha(HybridModel): >> pass >> >> >> class HybridBeta(HybridModel): >> pass >> >> >> class TestHybrids: >> >> def test_all_query(self): >> db.session.add(HybridType(name=HybridAlpha.hybrid_type_identity)) >> db.session.add(HybridType(name=HybridBeta.hybrid_type_identity)) >> db.session.add(HybridAlpha(name='alpha_instance')) >> db.session.add(HybridBeta(name='beta_instance')) >> all_alphas = HybridAlpha.query.all() >> assert HybridModel.query.count() == 2 >> assert HybridAlpha.query.count() == 1 >> assert HybridBeta.query.count() == 1 >> >> >> On Wed, Dec 28, 2016 at 2:19 PM, mike bayer <mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com>> wrote: >> >> >> >> On 12/27/2016 04:36 PM, Tucker Beck wrote: >> >> Mike (or whoever else might have insight): >> >> I used the pattern above, and it worked great until I tried use >> it for a >> really large table that I was querying and filtering by the hybrid >> property. Something like this: >> query(HybridModel).filter_by(hybrid_type_name='alpha') >> >> >> Well I mentioned a correlated subquery won't scale here. >> >> >> >> >> >> The resulting query does a seq-scan applying a sub-select to get >> the >> hybrid_type_name and then comparing it to the filter value >> ('alpha' in >> the example). Obviously, this is sub-optimal, and I want the >> query to do >> an index-scan comparing the hybrid_type_id matching the >> filter_value to >> the hybrid_type_id of each row. The table has an index on >> hybrid_type_id, so that query is orders of magnitude faster. >> Before >> using this revised pattern we discussed, I got the query I wanted >> by >> adding a comparator to my class that basically inverts the >> select used >> by the hybrid property expression. That worked great before >> using this >> pattern. >> >> >> I'm not sure what "invert the select" looks like. >> >> >> However, with the new pattern, the delcared_attr __mapper_args__ >> attempts to use the expression from the comaprator for >> polymorphic_on, >> and that query results in an integer (primary key) which won't >> work >> because we defined our polymorphic identities using the name >> >> >> I'm not understanding this part either, can this be demonstrated >> with the simple test script I gave you ? >> >> >> >> >> >> Do you have any ideas on how I might resolve this issue? >> >> On Sat, Dec 3, 2016 at 4:25 PM, mike bayer >> <mike...@zzzcomputing.com <mailto:mike...@zzzcomputing.com> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com>>> wrote: >> >> >> >> On 12/01/2016 05:29 PM, Tucker Beck wrote: >> >> This is what I ended up with, and it *seems* to work >> pretty >> well. Does >> my approach here pass the sniff test? >> >> >> everything looks idiomatic, so if it does what you need, >> then it's >> greatg! >> >> >> >> >> from sqlalchemy.ext.hybrid import hybrid_property >> from sqlalchemy.ext.declarative import declarative_base, >> declared_attr >> from sqlalchemy.orm import sessionmaker, relationship, >> column_property >> from sqlalchemy import ( >> Column, Integer, ForeignKey, Text, >> select, create_engine, >> ) >> >> Base = declarative_base() >> >> >> class HybridType(Base): >> __tablename__ = 'hybrid_types' >> id = Column(Integer, primary_key=True) >> name = Column(Text) >> >> >> class HybridModel(Base): >> __tablename__ = 'hybrids' >> hybrid_type_identity = 'base' >> >> id = Column(Integer, primary_key=True) >> name = Column(Text) >> hybrid_type_id = Column(Integer, >> ForeignKey('hybrid_types.id <http://hybrid_types.id> >> <http://hybrid_types.id> >> <http://hybrid_types.id>')) >> hybrid_type = relationship('HybridType') >> >> @declared_attr >> def __mapper_args__(cls): >> return dict( >> polymorphic_on=cls.hybrid_type_name, >> polymorphic_identity=cls.hybri >> d_type_identity, >> ) >> >> def __repr__(self): >> return "{} ({}:{})".format(type(self).__name__, >> self.name <http://self.name> <http://self.name> >> <http://self.name>, self.id <http://self.id> >> <http://self.id> <http://self.id>) >> >> def __init__(self, **kwargs): >> self.hybrid_type_name = self.hybrid_type_identity >> super(HybridModel, self).__init__(**kwargs) >> >> @hybrid_property >> def hybrid_type_name(self): >> return self.hybrid_type.name >> <http://self.hybrid_type.name> >> <http://self.hybrid_type.name> >> <http://self.hybrid_type.name> >> >> @hybrid_type_name.expression >> def hybrid_type_name(cls): >> return ( >> select([HybridType.name]). >> where(HybridType.id == cls.hybrid_type_id). >> as_scalar() >> ) >> >> @hybrid_type_name.setter >> def hybrid_type_name(self, value): >> self.hybrid_type_id = ( >> select([HybridType.id]). >> where(HybridType.name == value) >> ) >> >> >> class HybridAlpha(HybridModel): >> hybrid_type_identity = 'alpha' >> >> >> class HybridBeta(HybridModel): >> hybrid_type_identity = 'beta' >> >> >> engine = create_engine('sqlite:///:memory:', echo=False) >> Base.metadata.create_all(engine) >> Session = sessionmaker(bind=engine) >> session = Session() >> session.add(HybridType(name='base')) >> session.add(HybridType(name='alpha')) >> session.add(HybridType(name='beta')) >> session.add(HybridModel(name='base_instance')) >> session.add(HybridAlpha(name='alpha_instance')) >> session.add(HybridBeta(name='beta_instance')) >> print(session.query(HybridModel).all()) >> print(session.query(HybridAlpha).all()) >> print(session.query(HybridAlpha).one().hybrid_type_name) >> session.query(HybridAlpha).one().hybrid_type_name = >> 'beta' >> print(session.query(HybridBeta).all()) >> >> >> On Wed, Nov 30, 2016 at 7:20 AM, mike bayer >> <mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com> >> <mailto:mike...@zzzcomputing.com <mailto:mike...@zzzcomputing.com >> >> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com>>>> wrote: >> >> >> >> On 11/29/2016 07:31 PM, Tucker Beck wrote: >> >> Thanks for this! What allows the query to filter >> in the >> derived >> classes >> when using a column property instead of a hybrid >> property? Is it >> declaring the __mapper_args__ in the base class >> declaration >> instead of >> after it? >> >> >> Yea I don't think setting __mapper_args__ after the >> fact on the >> class does anything. Those __mapper_args__ are >> only for the >> mapper() constructor directly, which gets called >> when the >> class is >> declared. >> >> you can set the actual polymorphic_on property right >> now >> only using >> the special >> class.__mapper__._set_polymorphic_on(<something>), >> that's one of a very few setters that's available on >> the mapper >> after the fact and even then it's not fully public >> API right >> now. >> >> >> >> Also, it seems like having the hybrid_type_prop >> column >> property >> obviates >> the need for the hybrid property >> hybrid_type_name...that is, >> except for >> the setter. Is there a way to give a >> column_property a >> setter? >> >> >> a column_property against a SQL expression is >> strictly a >> read-only >> thing, since "writing" to it makes no sense in terms >> of >> persistence. >> >> >> the column_property aspect here isn't strictly >> necessary, >> the mapper >> will create the property internally as a special >> case for the >> polymorphic_on setting if not provided. the example >> can be >> re-organized w/ your hybrid in at least two ways: >> >> >> class HybridModel(Base): >> # ... >> >> @declared_attr >> def __mapper_args__(cls): >> return { >> "polymorphic_on": cls.hybrid_type_name >> } >> >> >> or >> >> >> class HybridModel(Base): >> # ... >> >> _hybrid_name_expr = ( >> select([HybridType.name]). >> where(HybridType.id == hybrid_type_id). >> as_scalar() >> ) >> >> __mapper_args__ = { >> "polymorphic_on": _hybrid_name_expr >> } >> >> # ... >> >> @hybrid_type_name.expression >> def hybrid_type_name(cls): >> return cls._hybrid_name_expr >> >> >> >> On Tue, Nov 29, 2016 at 2:59 PM, mike bayer >> <mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com>> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com> >> <mailto:mike...@zzzcomputing.com <mailto:mike...@zzzcomputing.com >> >>> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com>> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com>>>>> wrote: >> >> >> >> On 11/29/2016 05:01 PM, Tucker Beck wrote: >> >> The issue with using the foreign_key >> integer >> value as the >> discriminator >> is that you won't know what that is at >> class >> declaration >> time. The >> type_name, however, you can declare as a >> part of the >> class as >> you would >> with a normal string discriminator. I'm >> not sure >> how you >> would do a >> correlated subquery for the polymorphic_on >> attribute. I >> modified the >> query object so that I could filter >> results for >> the derived >> classes. It >> doesn't seem like that happens >> automatically. >> Maybe I'm >> just not >> doing >> it right. >> >> >> The correlated subquery will not scale as >> well as a >> direct >> identifier, but here is an adaption of your >> test using >> column_property(), and you should be able to >> set >> polymorphic_on to >> the select() object directly too. There's >> another >> example >> of this >> at >> >> >> >> http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html?hi >> ghlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on >> <http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html? >> highlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on> >> >> <http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html? >> highlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on >> <http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html? >> highlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on>> >> >> >> <http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html? >> highlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on >> <http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html? >> highlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on> >> >> <http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html? >> highlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on >> <http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html? >> highlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on>>> >> >> >> >> <http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html? >> highlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on >> <http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html? >> highlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on> >> >> <http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html? >> highlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on >> <http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html? >> highlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on>> >> >> >> <http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html? >> highlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on >> <http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html? >> highlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on> >> >> <http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html? >> highlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on >> <http://docs.sqlalchemy.org/en/latest/orm/mapping_api.html? >> highlight=polymorphic_on#sqlalchemy.orm.mapper.params.polymorphic_on>>>>. >> >> For the "use the id" approach, you would need >> to >> query from >> HybridType up front and populate the >> polymorphic_identity >> attributes >> after the fact, this is a feature that is >> not directly >> supported yet >> but there is a recipe to do so at >> >> >> >> https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settable >> -polymorphic-identity >> <https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settabl >> e-polymorphic-identity> >> >> <https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settabl >> e-polymorphic-identity >> <https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settabl >> e-polymorphic-identity>> >> >> >> <https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settabl >> e-polymorphic-identity >> <https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settabl >> e-polymorphic-identity> >> >> <https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settabl >> e-polymorphic-identity >> <https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settabl >> e-polymorphic-identity>>> >> >> >> >> <https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settabl >> e-polymorphic-identity >> <https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settabl >> e-polymorphic-identity> >> >> <https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settabl >> e-polymorphic-identity >> <https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settabl >> e-polymorphic-identity>> >> >> >> <https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settabl >> e-polymorphic-identity >> <https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settabl >> e-polymorphic-identity> >> >> <https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settabl >> e-polymorphic-identity >> <https://bitbucket.org/zzzeek/sqlalchemy/issues/2792/settabl >> e-polymorphic-identity>>>>. >> >> >> >> from sqlalchemy.ext.hybrid import >> hybrid_property >> from sqlalchemy.ext.declarative import >> declarative_base >> from sqlalchemy.orm import sessionmaker, >> relationship, >> column_property >> from sqlalchemy import ( >> Column, Integer, ForeignKey, Text, >> select, create_engine, >> ) >> >> Base = declarative_base() >> >> >> class HybridType(Base): >> __tablename__ = 'hybrid_types' >> id = Column(Integer, primary_key=True) >> name = Column(Text) >> >> >> class HybridModel(Base): >> __tablename__ = 'hybrids' >> >> id = Column(Integer, primary_key=True) >> name = Column(Text) >> hybrid_type_id = Column(Integer, >> ForeignKey('hybrid_types.id >> <http://hybrid_types.id> <http://hybrid_types.id> >> <http://hybrid_types.id> >> <http://hybrid_types.id>')) >> hybrid_type = relationship('HybridType') >> >> hybrid_type_prop = column_property( >> select([HybridType.name]). >> where(HybridType.id == >> hybrid_type_id). >> as_scalar() >> ) >> >> __mapper_args__ = { >> "polymorphic_on": hybrid_type_prop >> } >> >> def __repr__(self): >> return "{} >> ({}:{})".format(type(self).__name__, >> self.name <http://self.name> <http://self.name> >> <http://self.name> >> <http://self.name>, self.id <http://self.id> >> <http://self.id> >> <http://self.id> <http://self.id>) >> >> def __init__(self, **kwargs): >> self.hybrid_type_name = >> >> type(self).__mapper_args__['polymorphic_identity'] >> super(HybridModel, >> self).__init__(**kwargs) >> >> @hybrid_property >> def hybrid_type_name(self): >> return self.hybrid_type.name >> <http://self.hybrid_type.name> >> <http://self.hybrid_type.name> >> <http://self.hybrid_type.name> >> <http://self.hybrid_type.name> >> >> @hybrid_type_name.setter >> def hybrid_type_name(self, value): >> self.hybrid_type_id = ( >> select([HybridType.id]). >> where(HybridType.name == value) >> ) >> >> >> class HybridAlpha(HybridModel): >> __mapper_args__ = >> {'polymorphic_identity': 'alpha'} >> >> >> class HybridBeta(HybridModel): >> __mapper_args__ = >> {'polymorphic_identity': 'beta'} >> >> >> engine = create_engine('sqlite:///:memory:', >> echo=True) >> Base.metadata.create_all(engine) >> Session = sessionmaker(bind=engine) >> session = Session() >> session.add(HybridType(name='alpha')) >> session.add(HybridType(name='beta')) >> session.add(HybridAlpha(name=' >> alpha_instance')) >> session.add(HybridBeta(name='beta_instance')) >> print(session.query(HybridAlpha).all()) >> >> >> >> On Tue, Nov 29, 2016 at 7:16 AM, mike >> bayer >> <mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com>> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com>>> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com>> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com>>>> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com>> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com>>> >> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com>> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com> >> <mailto:mike...@zzzcomputing.com >> <mailto:mike...@zzzcomputing.com>>>>>> wrote: >> >> >> >> On 11/28/2016 05:58 PM, Tucker Beck >> wrote: >> >> Hello, I'm writing today about an >> interesting >> problem we ran >> into with >> our sqlalchemy based data store >> api. >> >> Our schema is based on the >> star-schema idea >> where we have a >> large 'fact >> table' with lots of rows. Within >> that table, >> each row has a >> foreign key >> to a small 'dimension table' in >> which >> each row >> has a unique >> name. Thus, >> the type of each row can be >> defined by the >> relationship >> between >> the fact >> row and the dimension row. >> >> We wanted to be able to use >> SQLAlchemy >> to add >> some custom >> functionality >> for the different types of rows >> we have >> in our 'fact >> table'. After >> learning about SQLAlchemy's >> inheritance >> models, I >> decided to see >> if we >> could support the schema that we >> had already >> devised for our >> project. >> The single-inheritance pattern >> seemed to >> fit the >> best, but I >> couldn't >> find a single case where someone >> was using a >> star-schema and >> needed the >> type discriminator to be derived >> from the >> foreign key to the >> dimension >> table. >> >> Further, I found as I was >> digging into the >> mechanics of the >> thing that >> you could not create a row in >> the fact table >> that was >> typed by the >> derived class at creation time. >> And, you >> cannot >> limit >> queries >> from the >> fact table by creating the queries >> against the >> derived >> classes. >> Suppose >> that (using declarative base) I >> have the >> fact table >> represented by a >> model called HybridModel. This >> model has two >> derived classes >> HybridAlpha >> and HybridBeta. I would like to >> be able to >> create a new >> row in >> the table >> wrapped by HybridModel by calling >> something like >> HybridAlpha(**kwargs) >> and have the type of the new row >> reflect the >> inheritance >> model I've >> described above. Next I wanted >> to be able to >> formulate a >> query >> against >> one of the derived models and >> have it >> limited by the >> type associated >> with the derived class. So, >> calling >> something like >> >> `session.query(HybridAlpha).all()` would >> only return >> rows with a >> type >> associated with the HybridAlpha >> model. >> >> After a lot of tinkering and >> experimentation, >> I've come >> up with the >> following solution: >> >> >> >> >> >> https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f8075 >> c75b3c89 >> <https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f807 >> 5c75b3c89> >> >> <https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f807 >> 5c75b3c89 >> <https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f807 >> 5c75b3c89>> >> >> >> <https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f807 >> 5c75b3c89 >> <https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f807 >> 5c75b3c89> >> >> <https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f807 >> 5c75b3c89 >> <https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f807 >> 5c75b3c89>>> >> >> >> >> <https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f807 >> 5c75b3c89 >> <https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f807 >> 5c75b3c89> >> >> <https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f807 >> 5c75b3c89 >> <https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f807 >> 5c75b3c89>> >> >> >> <https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f807 >> 5c75b3c89 >> <https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f807 >> 5c75b3c89> >> >> <https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f807 >> 5c75b3c89 >> <https://gist.github.com/dusktreader/2658dedb8ca8e2fd941f807 >> 5c75b3c89>>>> > > -- -=Tucker A. Beck=- Illustrious Writer Devious Coder Last Hope for the Free World Also, Modest -- SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper http://www.sqlalchemy.org/ To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description. --- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.