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.

Reply via email to