Hey Mike, I just saw the new selectinload feature, and am super excited about it!
As far as I can tell, this is only for relationships, so I wanted to check what the plans are for an inheritance loader using selectinload as well, given that you initially mentioned them together and I can't find an issue in the tracker for it. > I've created an issue for both a new relationship loader and an > inheritance loader at the same time, since they will use very similar > paths, at https://bitbucket.org/zzzeek/sqlalchemy/issues/3944 > <https://www.google.com/url?q=https%3A%2F%2Fbitbucket.org%2Fzzzeek%2Fsqlalchemy%2Fissues%2F3944&sa=D&sntz=1&usg=AFQjCNHP34Q9-nl0O_qmczm7uZFo8daCAA> > . Thanks! Damon On Tuesday, April 4, 2017 at 8:24:07 PM UTC-7, Mike Bayer wrote: > > Reusing the original query is hard, and the case you have is due to > mismatched entities that would have to be handled, probably by wrapping the > original query in a subquery just like subquery eager loading does. The > subquery eager loading feature took a few years to work out an enormous > number of issues with this approach. > > The new feature which I'm now working on, several hours a day, in response > to this thread solves the whole problem in a much better way, using a > simple IN expression against the primary keys of all states loaded in the > query. Betas of 1.2 will hopefully be available in a month or two. The > feature can be replicated in older versions by just grabbing the list of > states coming out of __iter__ and using their primary keys in an IN clause. > Parallel development of this other approach as an interim throwaway is > probably not worth it if it continues to become more complicated. > > On Apr 4, 2017 7:02 PM, <da...@benchling.com <javascript:>> wrote: > > Hey Mike, > > Thanks for those -- seems to have helped those cases, though we're running > into some pretty weird behavior with joins. Here's a simplified case that > shows one of the issues we're running into (use _loader_from_cls from > above): > > > class W(Base): > __tablename__ = 'w' > id = Column(Integer, primary_key=True) > type = Column(String) > > x_id = Column(Integer, ForeignKey('x.id')) > x = relationship('X') > > __mapper_args__ = {'polymorphic_on': type} > > > class W2(W): > __tablename__ = 'w2' > id = Column(Integer, ForeignKey('w.id'), primary_key=True) > w2 = Column(String) > __mapper_args__ = {'polymorphic_identity': 'w2'} > > > class X(Base): > __tablename__ = 'x' > id = Column(Integer, primary_key=True) > > > @event.listens_for(W, "load", propagate=True) > def load_extra(target, context): > key = ('loader_by_cls', type(target)) > > if key not in context.attributes: > context.attributes[key] = _loader_for_cls(target, context) > > > e = create_engine("sqlite://", echo=True) > Base.metadata.create_all(e) > s = Session(e) > > s.add(W2(x=X())) > s.commit() > > s.query(W).join(W.x).first() > > > One of the statements emitted by this is > > SELECT w2.id AS w2_id, w.id AS w_id, w.type AS w_type, w.x_id AS w_x_id, > w2.w2 AS w2_w2 > FROM w2, w JOIN x ON x.id = w.x_id > LIMIT ? OFFSET ? > > Specifically, the "FROM w2, w JOIN x" is not what we want here -- we'd > just want "FROM w2" > > Replacing > > q = orig_query.with_entities(target_cls) > > with > > q = context.session.query(target_cls).join(orig_query.subquery()) > > fixes the issue, though this feels less than ideal. Subclassing Query also > seems less than ideal. Do you have any ideas here? > > > Thanks, > Damon > > On Tuesday, April 4, 2017 at 6:42:32 AM UTC-7, Mike Bayer wrote: > >> This repaste of the example contains two refinements to address each of >> these issues distinctly. We want to avoid re-entrant invocation of >> _loader_for_cls, so putting a flag into attributes handles that. Then >> there's the issue of same class coming in that we are already querying, >> we can look at the type being loaded in column_descriptions, and this >> also solves the re-entrant problem though I think the flag is more >> direct. The column_descriptions check likely needs more refinements, >> but the general idea is that if the entity being loaded is already a B2, >> then you wouldn't do any extra B2 queries (unless you have multiple >> levels of inheritance / polymorphic loading in which case that probably >> needs adjustment). >> >> The "strong reference" comment, you can try commenting that out in the >> test and see what happens. Because this loader trick loads all the >> subclasses up front upon seeing the first member of a particular >> subtype, it is loading for subsequent instances that aren't seen yet as >> well. If we don't make a strong reference to them, they get lost and >> the extra attribute loading fails. >> >> The feature I have in development works a lot better because all the >> additional loads are done *after* all the entities are loaded, and it >> does it using an IN query that only includes those entities that >> definitely need the load. I'm not sure if there are other negative side >> effects from our loading of the "subclass" entity in some cases ahead of >> where the primary query gets at the entity. The recipe here could be >> made to do all the extra loads after the primary query but theres no >> "after query" hook, you'd have to subclass Query and override __iter__ >> to add this step. >> >> from sqlalchemy import * >> from sqlalchemy.orm import * >> from sqlalchemy.ext.declarative import declarative_base >> from sqlalchemy import event >> >> Base = declarative_base() >> >> >> class A(Base): >> __tablename__ = 'a' >> id = Column(Integer, primary_key=True) >> a1 = Column(String) >> type = Column(String) >> >> __mapper_args__ = {'polymorphic_on': type} >> >> >> class B1(A): >> __tablename__ = 'b1' >> id = Column(Integer, ForeignKey('a.id'), primary_key=True) >> b1 = Column(String) >> b_data = Column(String) >> __mapper_args__ = {'polymorphic_identity': 'b1'} >> >> >> class B2(A): >> __tablename__ = 'b2' >> id = Column(Integer, ForeignKey('a.id'), primary_key=True) >> b2 = Column(String) >> b_data = Column(String) >> cs = relationship("C", lazy='subquery') >> >> __mapper_args__ = {'polymorphic_identity': 'b2'} >> >> >> class C(Base): >> __tablename__ = 'c' >> id = Column(Integer, primary_key=True) >> b2_id = Column(ForeignKey('b2.id')) >> >> >> class B3(A): >> __tablename__ = 'b3' >> id = Column(Integer, ForeignKey('a.id'), primary_key=True) >> b3 = Column(String) >> b_data = Column(String) >> __mapper_args__ = {'polymorphic_identity': 'b3'} >> >> >> def _loader_for_cls(target, context): >> orig_query = context.query >> >> target_cls = type(target) >> >> if orig_query.column_descriptions[0]['type'] is target_cls: >> return None >> >> # take the original query and chance the entity to the subclass >> q = orig_query.with_entities(target_cls) >> >> # defer everything that's not PK / polymorphic_on from A. this >> whole >> # bit is just to avoid all those extra columns >> to_defer = [] >> mapper = inspect(target).mapper >> inherited = mapper.inherits >> while inherited is not None: >> for attr in inherited.column_attrs: >> if not attr.expression.primary_key and \ >> attr.expression is not inherited.polymorphic_on: >> to_defer.append(attr.key) >> for attr in inherited.relationships: >> to_defer.append(attr.key) >> inherited = inherited.inherits >> q = q.options(*[defer(k) for k in to_defer]) >> >> q._attributes["load_extra_called"] = True >> >> # store this strong reference so recs don't get lost while >> # iterating >> return q.all() >> >> >> @event.listens_for(A, "load", propagate=True) >> def load_extra(target, context): >> if "load_extra_called" in context.attributes: >> return >> >> key = ('loader_by_cls', type(target)) >> >> if key not in context.attributes: >> context.attributes[key] = _loader_for_cls(target, context) >> >> >> e = create_engine("sqlite://", echo=True) >> Base.metadata.create_all(e) >> >> >> s = Session(e) >> s.add_all([ >> B1(b_data='b11', b1='b1', a1='b11'), >> B2(b_data='b21', b2='b2', a1='b21', cs=[C(), C(), C()]), >> B3(b_data='b31', b3='b3', a1='b31'), >> B1(b_data='b12', b1='b1', a1='b12'), >> B1(b_data='b13', b1='b1', a1='b13'), >> B2(b_data='b22', b2='b2', a1='b22', cs=[C(), C()]), >> B3(b_data='b32', b3='b3', a1='b12'), >> B3(b_data='b33', b3='b3', a1='b33') >> ]) >> s.commit() >> >> >> q = s.query(A).filter(A.a1.like('%2%')) >> >> result = q.all() >> >> print "----- no more SQL ----" >> for b in result: >> if isinstance(b, B1): >> print b.b1 >> elif isinstance(b, B2): >> print b.cs >> print b.b2 >> elif isinstance(b, B3): >> print b.b3 >> >> >> >> >> On 04/04/2017 12:16 AM, da...@benchling.com wrote: >> > Hey Mike, >> > >> > Looks like I spoke too soon -- a few more questions: >> > >> > Using the example code you posted, we're actually seeing 4 additional >> > queries (one per result model), rather than the expected 3 (one per >> > result model type). If you print context.query inside load_extra, I >> > think it's clear why: >> > >> > - the loader sequentially processes loaded models >> > ( >> https://github.com/zzzeek/sqlalchemy/blob/master/lib/sqlalchemy/orm/loading.py#L75) >> >> >> > - on processing the first B2, it executes a query to load all B2's data >> > - when processing the results of this new query, it now processes the >> > second B2 record (under a new context) >> > - load_extra() gets called for the new B2 with a different context than >> > the first B2 >> > - this now executes _loader_for_cls >> > >> > Note that if you don't call _loader_for_cls in load_extra, load_extra >> is >> > called for each record using the correct context. >> > >> > A related issue is that if you query directly for B2, it'll redundantly >> > execute _loader_for_cls. >> > >> > I think we could solve both of these issues by doing this instead: >> > >> > q = orig_query.with_entities(target_cls) >> > if q == orig_query: >> > return >> > >> > Unfortunately, the == operator doesn't work for comparing queries. Do >> > you have a way to compare query equality, or alternatively have a >> > solution to both of these issues? >> > >> > Could you also elaborate on what you meant by this comment? >> > >> > # store this strong reference so recs don't get lost while >> > # iterating >> > >> > >> > Thanks again for all your help, >> > Damon >> > >> > On Monday, April 3, 2017 at 6:42:17 PM UTC-7, da...@benchling.com >> wrote: >> > >> > Thanks a ton for your help, Mike! >> > >> > We played around with it and are pretty happy with your solution >> > using the load() event, so we'll be using that moving forward. >> > >> > >> > Damon >> > >> > On Wednesday, March 29, 2017 at 2:40:39 PM UTC-7, Mike Bayer wrote: >> > >> > I have a working version of both loading relationships via IN >> as >> > well as >> > loading joined inheritance subclasses via IN, including your >> > great idea >> > that extra eager loaders should continue to work for the >> subclass >> > loaders. >> > >> > I've only tested it with one scenario so far and both patches >> > have a >> > long way to go re: tests and documentation. >> > >> > the scratch test case is currently in the commit message for >> the >> > second >> > patch, which illustrates a base class + subclass load where >> both >> > classes >> > have an additional relationship. This is at >> > https://gerrit.sqlalchemy.org/#/c/359/ >> > <https://gerrit.sqlalchemy.org/#/c/359/>, note this builds >> upon the >> > previous gerrit at https://gerrit.sqlalchemy.org/#/c/352/ >> > <https://gerrit.sqlalchemy.org/#/c/352/>. The work >> > at the moment will likely fall down with any kind of surprises >> > but if >> > you wanted to start running it and finding those cases, that is >> > always >> > helpful. >> > >> > So far this looks promising as something that can be in 1.2 >> with >> > perhaps >> > some "EXPERIMENTAL" warnings surrounding it, but overall 1.2 >> was >> > lacking >> > a "killer ORM feature" so these two would be it. >> > >> > >> > >> > On 03/23/2017 06:02 PM, mike bayer wrote: >> > > >> > > >> > > On 03/23/2017 02:40 PM, mike bayer wrote: >> > >> >> > >> >> > >> On 03/23/2017 12:53 PM, da...@benchling.com wrote: >> > >>> Hey Mike, >> > >>> >> > >>> Thanks for the quick response! >> > >>> >> > >>> For developers that are pretty familiar with the SQLAlchemy >> > API, but not >> > >>> so much the internals, would implementing the subqueryloads >> to >> > >>> contribute to SA be a reasonable endeavor? Could you >> > ballpark how much >> > >>> time how long it might take for us to do it? >> > >> >> > >> I haven't looked at what this would take, but it would be >> > intricate and >> > >> also need a lot of tests that are not easy to write. >> Like, >> > if you >> > >> worked on it, you could probably get something working, but >> > then that >> > >> probably wouldn't be how it really needs to be because all >> > kinds of >> > >> things that are simple for simple cases don't work with the >> > vast amount >> > >> of edge cases which we have. >> > > >> > > I've created an issue for both a new relationship loader and >> an >> > > inheritance loader at the same time, since they will use very >> > similar >> > > paths, at https://bitbucket.org/zzzeek/sqlalchemy/issues/3944 >> > <https://bitbucket.org/zzzeek/sqlalchemy/issues/3944>. A >> POC >> > > for the relationship loader so far looks to be very simple >> > (but then >> > > again all the loaders start out very simple...) and is at >> > > https://gerrit.sqlalchemy.org/352 >> > <https://gerrit.sqlalchemy.org/352>. The same infrastructure >> and >> > > approach would also be used for the mapper inheritance >> loader, >> > which >> > > would be enabled via a mapper()-level flag, as well as >> > per-query using a >> > > new query option. >> > > >> > > I can't yet guarantee this will be a 1.2 thing, I'd have to >> > get some >> > > more momentum going w/ test cases and all that. In a 1.2 >> > release it >> > > would also be a little bit experimental as new loading styles >> > usually >> > > have small issues coming up for months or years, as people >> try >> > more use >> > > cases. >> > > >> > > >> > > >> > > >> > > >> > > >> > >> >> > >> The routine that's loading the additional columns just for >> > one object at >> > >> a time is here: >> > >> >> > >> >> > >> https://github.com/zzzeek/sqlalchemy/blob/master/lib/sqlalchemy/orm/loading.py#L635 >> >> > < >> https://github.com/zzzeek/sqlalchemy/blob/master/lib/sqlalchemy/orm/loading.py#L635> >> >> >> > >> > >> >> > >> and then here for the bulk of it: >> > >> >> > >> https://github.com/zzzeek/sqlalchemy/blob/master/lib/sqlalchemy/orm/mapper.py#L2588 >> >> > < >> https://github.com/zzzeek/sqlalchemy/blob/master/lib/sqlalchemy/orm/mapper.py#L2588> >> >> >> > >> > >> >> > >> >> > >> >> > >> But the much harder part would be how to work this step into >> > the loading >> > >> infrastructure, which would be somewhere in >> > >> >> > >> https://github.com/zzzeek/sqlalchemy/blob/master/lib/sqlalchemy/orm/loading.py#L273 >> >> > < >> https://github.com/zzzeek/sqlalchemy/blob/master/lib/sqlalchemy/orm/loading.py#L273>, >> >> >> > >> > >> >> > >> which is a very intricate function with over a decade of >> > constant >> > >> refactorings behind it, and I'd have to think pretty deeply >> > for awhile >> > >> how best to do this. >> > >> >> > >> Not to mention that there's more than one way to do this >> > query, there's >> > >> either re-using the criteria from the original query, or >> there's >> > >> injecting the primary key ids of the whole list of objects >> > into an IN >> > >> clause after the fact. The latter approach is probably more >> > efficient >> > >> but wouldn't work for composite primary keys outside of >> > Postgresql. As >> > >> a built in feature I'd want "IN" loading to be an option at >> > least. >> > >> >> > >> >> > >> >> > >>> >> > >>> Regarding STI and relationships, is there any way to do >> that >> > but still >> > >>> get the benefits of JTI? e.g. is there an easy way to >> resolve >> > >>> my_base_class_inst.subclass_prop as a proxy to the >> subclass? >> > We could >> > >>> roll our own using __getitem__ but it seems a little hairy. >> > >> >> > >> So proxying to a related item wouldn't be terrible, sure you >> > could use >> > >> __getitem__ or you could also add descriptors to the primary >> > class, >> > >> adding the descriptors to match the "info" could be >> automated >> > as well >> > >> (or even do it in the other direction, add special >> descriptor >> > to main >> > >> class == a column gets added to related class). >> > >> >> > >> >> > >> Doing the thing where you query() for all the related >> classes >> > after the >> > >> fact yourself might not be that terrible. you can use the >> > load() event >> > >> which receives the query context that has the query you need >> > already. I >> > >> guess it's time for proof of concept. Here's that. You >> > can see at >> > >> the end we load all the "bs" without any per-object load. >> > >> >> > >> from sqlalchemy import * >> > >> from sqlalchemy.orm import * >> > >> from sqlalchemy.ext.declarative import declarative_base >> > >> from sqlalchemy import event >> > >> >> > >> Base = declarative_base() >> > >> >> > >> >> > >> class A(Base): >> > >> __tablename__ = 'a' >> > >> id = Column(Integer, primary_key=True) >> > >> a1 = Column(String) >> > >> type = Column(String) >> > >> >> > >> __mapper_args__ = {'polymorphic_on': type} >> > >> >> > >> >> > >> class B1(A): >> > >> __tablename__ = 'b1' >> > >> id = Column(Integer, ForeignKey('a.id <http://a.id>'), >> > primary_key=True) >> > >> b1 = Column(String) >> > >> b_data = Column(String) >> > >> __mapper_args__ = {'polymorphic_identity': 'b1'} >> > >> >> > >> >> > >> class B2(A): >> > >> __tablename__ = 'b2' >> > >> id = Column(Integer, ForeignKey('a.id <http://a.id>'), >> > primary_key=True) >> > >> b2 = Column(String) >> > >> b_data = Column(String) >> > >> cs = relationship("C", lazy='subquery') >> > >> >> > >> __mapper_args__ = {'polymorphic_identity': 'b2'} >> > >> >> > >> >> > >> class C(Base): >> > >> __tablename__ = 'c' >> > >> id = Column(Integer, primary_key=True) >> > >> b2_id = Column(ForeignKey('b2.id <http://b2.id>')) >> > >> >> > >> >> > >> class B3(A): >> > >> __tablename__ = 'b3' >> > >> id = Column(Integer, ForeignKey('a.id <http://a.id>'), >> > primary_key=True) >> > >> b3 = Column(String) >> > >> b_data = Column(String) >> > >> __mapper_args__ = {'polymorphic_identity': 'b3'} >> > >> >> > >> >> > >> def _loader_for_cls(target, context): >> > >> orig_query = context.query >> > >> >> > >> target_cls = type(target) >> > >> >> > >> # take the original query and chance the entity to the >> > subclass >> > >> q = orig_query.with_entities(target_cls) >> > >> >> > >> # defer everything that's not PK / polymorphic_on from >> A. >> > this whole >> > >> # bit is just to avoid all those extra columns >> > >> to_defer = [] >> > >> mapper = inspect(target).mapper >> > >> inherited = mapper.inherits >> > >> while inherited is not None: >> > >> for attr in inherited.column_attrs: >> > >> if not attr.expression.primary_key and \ >> > >> attr.expression is not >> > inherited.polymorphic_on: >> > >> to_defer.append(attr.key) >> > >> for attr in inherited.relationships: >> > >> to_defer.append(attr.key) >> > >> inherited = inherited.inherits >> > >> q = q.options(*[defer(k) for k in to_defer]) >> > >> >> > >> # store this strong reference so recs don't get lost >> while >> > >> # iterating >> > >> return q.all() >> > >> >> > >> >> > >> @event.listens_for(A, "load", propagate=True) >> > >> def load_extra(target, context): >> > >> key = ('loader_by_cls', type(target)) >> > >> >> > >> if key not in context.attributes: >> > >> context.attributes[key] = _loader_for_cls(target, >> > context) >> > >> >> > >> >> > >> e = create_engine("sqlite://", echo=True) >> > >> Base.metadata.create_all(e) >> > >> >> > >> >> > >> s = Session(e) >> > >> s.add_all([ >> > >> B1(b_data='b11', b1='b1', a1='b11'), >> > >> B2(b_data='b21', b2='b2', a1='b21', cs=[C(), C(), C()]), >> > >> B3(b_data='b31', b3='b3', a1='b31'), >> > >> B1(b_data='b12', b1='b1', a1='b12'), >> > >> B1(b_data='b13', b1='b1', a1='b13'), >> > >> B2(b_data='b22', b2='b2', a1='b22', cs=[C(), C()]), >> > >> B3(b_data='b32', b3='b3', a1='b12'), >> > >> B3(b_data='b33', b3='b3', a1='b33') >> > >> ]) >> > >> s.commit() >> > >> >> > >> >> > >> q = s.query(A).filter(A.a1.like('%2%')) >> > >> >> > >> result = q.all() >> > >> >> > >> print "----- no more SQL ----" >> > >> for b in result: >> > >> if isinstance(b, B1): >> > >> print b.b1 >> > >> elif isinstance(b, B2): >> > >> print b.cs >> > >> print b.b2 >> > >> elif isinstance(b, B3): >> > >> print b.b3 >> > >> >> > >> >> > >> >> > >> >> > >> >> > >>> >> > >>> >> > >>> Thanks again, >> > >>> Damon >> > >>> >> > >>> On Wednesday, March 22, 2017 at 3:59:45 PM UTC-7, Mike >> Bayer >> > wrote: >> > >>> >> > >>> >> > >>> >> > >>> On 03/22/2017 02:17 PM, da...@benchling.com >> > <javascript:> wrote: >> > >>> > Hey all, >> > >>> > >> > >>> > We were wondering if you had any advice on having a >> > large (~10) >> > >>> number >> > >>> > of polymorphic subclasses for a single base class. >> Using >> > >>> > with_polymorphic: '*' causes SQLAlchemy to joinedload >> all >> > >>> subclasses >> > >>> > like this: >> > >>> > >> > >>> > SELECT ... >> > >>> > FROM base_table >> > >>> > LEFT OUTER JOIN sub_table_1 ON base_table.id >> > <http://base_table.id> >> > >>> <http://base_table.id> = sub_table_1.id >> > <http://sub_table_1.id> <http://sub_table_1.id> >> > >>> > LEFT OUTER JOIN sub_table_2 ON base_table.id >> > <http://base_table.id> >> > >>> <http://base_table.id> = sub_table_2.id >> > <http://sub_table_2.id> <http://sub_table_2.id> >> > >>> > ... >> > >>> > >> > >>> > Postgres buckles under too many joins, and these >> > queries start >> > >>> taking a >> > >>> > really long time. >> > >>> > >> > >>> > One other note is that for most of our queries, only >> a >> > few of >> > >>> these >> > >>> > sub-tables are actually needed, so most of the joins >> > are wasted. >> > >>> > Unfortunately, ahead of time, we don't know which >> > tables will be >> > >>> needed >> > >>> > -- we're relying on the discriminator. >> > >>> > >> > >>> > Ideally, we'd be able to specify that the ORM should >> > >>> subqueryload the >> > >>> > subclasses (and only execute subqueries on the types >> > that are >> > >>> present). >> > >>> > This would have to happen both when querying the base >> > table, but >> > >>> also >> > >>> > when accessing relationships. We'd want it to execute >> > a query on >> > >>> the >> > >>> > base table, then execute one query for each present >> > subclass. >> > >>> > >> > >>> > Another solution might be to use some kind of hook >> that >> > >>> > >> > >>> > - is executed after a query returns with results (or >> > after a >> > >>> list of >> > >>> > models are added to the session?) >> > >>> > - groups the models by type and runs its own >> > subqueries to load >> > >>> the data >> > >>> > >> > >>> > Any help here is greatly appreciated! >> > >>> >> > >>> >> > >>> The purpose of with_polymorphic is more about being >> able to >> > >>> filter on >> > >>> multiple classes at the same time, which is why it uses >> > joins, but >> > >>> these >> > >>> don't scale to many subclasses. Adding a subquery >> > load for the >> > >>> related tables would be something that the ORM can >> > someday have as a >> > >>> feature, but it would need a lot of tests to ensure >> it's >> > working as >> > >>> advertised. >> > >>> >> > >>> There's a lot of ways to get those other tables loaded >> > but none of >> > >>> them >> > >>> look that great. Turning off with_polymorphic(), one >> > approach >> > >>> is to >> > >>> collect all the distinct types and identifiers from >> your >> > query >> > >>> result; >> > >>> then do a separate query for each subtype: >> > >>> >> > >>> result = >> session.query(BaseClass).filter(...).all() >> > >>> >> > >>> types = sorted([(type(obj), obj.id >> > <http://obj.id> <http://obj.id>) for obj >> > >>> in result], >> > >>> key=lambda t: t[0]) >> > >>> >> > >>> for type, ids in itertools.groupby(types, >> > key=lambda t: >> > >>> t[0]): >> > >>> >> > session.query(type).filter(type.id.in_(ids)).all() >> > >>> >> > >>> That will emit a query with an INNER JOIN for each >> class >> > and will >> > >>> populate the remaining records in the identity map. >> The >> > columns >> > >>> that >> > >>> are already loaded are not re-accessed, though the >> DBAPI >> > will still >> > >>> send >> > >>> them over the network to the cursor. You can try >> > limiting the >> > >>> columns >> > >>> you query for in each statement as well by using the >> > defer() option. >> > >>> >> > >>> Another way is to use with_polymorphic() but to provide >> > a different >> > >>> kind >> > >>> of SQL statement, like a polymorphic_union(). This >> > would be a >> > >>> UNION of >> > >>> statements that each have an inner join. the >> resulting >> > SQL is a >> > >>> beast >> > >>> but it at least isn't using those left outer joins. I >> > think you >> > >>> can >> > >>> probably use sqlalchemy.orm.util.polymorphic_union() >> > directly to get >> > >>> this UNION statement built up automatically. >> > >>> >> > >>> Still another way is to reorganize the mappings to use >> > single-table >> > >>> inheritance and relationship() to link out to the >> > related table, >> > >>> then >> > >>> the normal "subqueryload" feature to load them as >> > relationships. >> > >>> Even >> > >>> though this way is ugly, I might use this (short of >> > implementing the >> > >>> related table subqueryload feature) just to make things >> > simple. >> > >>> >> > >>> >> > >>> Definitely a feature that should be added but that's >> not an >> > >>> immediate >> > >>> solution. >> > >>> >> > >>> > >> > >>> > >> > >>> > Thanks, >> > >>> > Damon >> > >>> > >> > >>> > -- >> > >>> > 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 >> > <http://stackoverflow.com/help/mcve> >> > >>> <http://stackoverflow.com/help/mcve >> > <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+...@googlegroups.com >> <javascript:> >> > >>> > <mailto:sqlalchemy+unsubscr...@googlegroups.com >> > <javascript:>>. >> > >>> > To post to this group, send email to >> > sqlal...@googlegroups.com >> > >>> <javascript:> >> > >>> > <mailto:sqlal...@googlegroups.com <javascript:>>. >> > >>> > Visit this group at >> > https://groups.google.com/group/sqlalchemy >> > <https://groups.google.com/group/sqlalchemy> >> > >>> <https://groups.google.com/group/sqlalchemy >> > <https://groups.google.com/group/sqlalchemy>>. >> > >>> > For more options, visit >> > https://groups.google.com/d/optout >> > <https://groups.google.com/d/optout> >> > >>> <https://groups.google.com/d/optout >> > <https://groups.google.com/d/optout>>. >> > >>> >> > >>> -- >> > >>> 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 >> > <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+...@googlegroups.com >> > >>> <mailto:sqlalchemy+unsubscr...@googlegroups.com>. >> > >>> To post to this group, send email to >> sqlal...@googlegroups.com >> > >>> <mailto:sqlal...@googlegroups.com>. >> > >>> Visit this group at >> > https://groups.google.com/group/sqlalchemy >> > <https://groups.google.com/group/sqlalchemy>. >> > >>> For more options, visit https://groups.google.com/d/optout >> > <https://groups.google.com/d/optout>. >> > >> > -- >> > 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+...@googlegroups.com >> > <mailto:sqlalchemy+unsubscr...@googlegroups.com>. >> > To post to this group, send email to sqlal...@googlegroups.com >> > <mailto:sqlal...@googlegroups.com>. >> > Visit this group at https://groups.google.com/group/sqlalchemy. >> > For more options, visit https://groups.google.com/d/optout. >> > -- > 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+...@googlegroups.com <javascript:>. > To post to this group, send email to sqlal...@googlegroups.com > <javascript:>. > > Visit this group at https://groups.google.com/group/sqlalchemy. > For more options, visit https://groups.google.com/d/optout. > > > -- 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.