On Tue, Jul 18, 2017 at 2:13 PM, <vin...@benchling.com> wrote: > Hello, I'm running into some difference in behavior for .exists() in > SQLAlchemy 1.0.17 and 1.1.11, related to subqueryloads. > > > class A(db.Model): > __tablename__ = 'a' > > id = db.Column(db.Integer, primary_key=True) > b_id = db.Column(db.Integer, db.ForeignKey('b.id'), nullable=False) > b = db.relationship('B', lazy='subquery') > > > class B(db.Model): > __tablename__ = 'b' > > id = db.Column(db.Integer, primary_key=True) > > > In SQLAlchemy 1.0.17, I can do all of the following: > > A.query.exists() # Note that A.b has lazy='subquery' > A.query.options(subqueryload(A.b)).exists() > > # We usually use this in the context of: > db.session.query(A.query.exists()).scalar()
There is a bug here in add_column() / subqueryload added as https://bitbucket.org/zzzeek/sqlalchemy/issues/4033/dont-crash-for-add_columns-when. however if you go into your 1.0 version and instead do lazy="joined" you'll see your EXISTS query rendering out the LEFT OUTER JOIN entirely unnecessarily. Current API when you're turning the query into a subquery or exists is to call enable_eagerloads(False): print s.query(A).enable_eagerloads(False).exists() the method is documented as such: https://docs.sqlalchemy.org/en/latest/orm/query.html?highlight=enable_eagerloads#sqlalchemy.orm.query.Query.enable_eagerloads however, I can't think of any reason public use of .subquery() or .exists() would ever *want* eagerloaded options to take place since by definition .subquery() and .exists() are never used to load objects. So in 1.2 I'll propose to make this automatic, that's https://bitbucket.org/zzzeek/sqlalchemy/issues/4032/enable_eagerloads-false-automatically-for. > > In SQLALchemy 1.1.11, we run into the following error: > A.query.exists() # Note that A.b has lazy='subquery' > > .../lib/python2.7/site-packages/sqlalchemy/orm/query.pyc in exists(self) > 3046 # .with_only_columns() after we have a core select() so that > 3047 # we get just "SELECT 1" without any entities. > -> 3048 return sql.exists(self.add_columns('1').with_labels(). > 3049 statement.with_only_columns([1])) > 3050 > > .../lib/python2.7/site-packages/sqlalchemy/orm/query.pyc in statement(self) > 465 """ > 466 > --> 467 stmt = self._compile_context(labels=self._with_labels).\ > 468 statement > 469 if self._params: > > .../lib/python2.7/site-packages/sqlalchemy/orm/query.pyc in > _compile_context(self, labels) > 3363 > 3364 for entity in self._entities: > -> 3365 entity.setup_context(self, context) > 3366 > 3367 for rec in context.create_eager_joins: > > .../lib/python2.7/site-packages/sqlalchemy/orm/query.pyc in > setup_context(self, query, context) > 3725 with_polymorphic=self._with_polymorphic, > 3726 only_load_props=query._only_load_props, > -> 3727 > polymorphic_discriminator=self._polymorphic_discriminator) > 3728 > 3729 def __str__(self): > > .../lib/python2.7/site-packages/sqlalchemy/orm/loading.pyc in > _setup_entity_query(context, mapper, query_entity, path, adapter, > column_collection, with_polymorphic, only_load_props, > polymorphic_discriminator, **kw) > 257 column_collection=column_collection, > 258 memoized_populators=quick_populators, > --> 259 **kw > 260 ) > 261 > > .../lib/python2.7/site-packages/sqlalchemy/orm/interfaces.pyc in setup(self, > context, entity, path, adapter, **kwargs) > 515 else: > 516 strat = self.strategy > --> 517 strat.setup_query(context, entity, path, loader, adapter, > **kwargs) > 518 > 519 def create_row_processor( > > .../lib/python2.7/site-packages/sqlalchemy/orm/strategies.pyc in > setup_query(self, context, entity, path, loadopt, adapter, > column_collection, parentmapper, **kwargs) > 786 orig_query, leftmost_mapper, > 787 leftmost_attr, leftmost_relationship, > --> 788 entity.entity_zero > 789 ) > 790 > > .../lib/python2.7/site-packages/sqlalchemy/orm/strategies.pyc in > _generate_from_original_query(self, orig_query, leftmost_mapper, > leftmost_attr, leftmost_relationship, orig_entity) > 859 ent['entity'] for ent in > orig_query.column_descriptions > 860 ])), > --> 861 False > 862 ) > 863 > > .../lib/python2.7/site-packages/sqlalchemy/orm/query.pyc in > _set_select_from(self, obj, set_base_alias) > 191 > 192 for from_obj in obj: > --> 193 info = inspect(from_obj) > 194 if hasattr(info, 'mapper') and \ > 195 (info.is_mapper or info.is_aliased_class): > > .../lib/python2.7/site-packages/sqlalchemy/inspection.pyc in > inspect(subject, raiseerr) > 73 "No inspection system is " > 74 "available for object of type %s" % > ---> 75 type_) > 76 return ret > 77 > > NoInspectionAvailable: No inspection system is available for object of type > <type 'NoneType'> > > > In particular, a subqueried relationship (either through lazy='subquery' or > subqueryload(A.b)) causes .exists() to fail. We obviously don't call > subqueryload(A.b) directly with exists queries, but we have models using > lazy='subquery' that we use .exists() queries for. > > My current workaround is A.query.options(lazyload('*')).exists(). Does this > seem like a bug introduced in 1.1, or were there any intentional changes > regarding use of .exists() queries? > > Thanks! > > -- > 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. -- 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.