On Tue, Jan 8, 2019 at 4:16 PM Gmoney <grgg...@gmail.com> wrote: > > Unable to limit the columns returned from a query using .load_only or > .with_entities. I feel like I'm missing a key concept here, and could use > some guidance. > > One column of interest is a relationship - which I'm guessing is my problem. > class Task(Base): > __tablename__ = 'TASK' > id = Column('ID', VARCHAR2(250), primary_key=True) > subject = Column('SUBJECT', VARCHAR2(250)) > comments =relationship('Comment', back_populates='task', lazy='joined') > > class Comment(Base): > __tablename__ = 'COMMENT' > id = Column('ID', Integer, primary_key=True) > msg_id = Column('MSG_ID', VARCHAR2(250), ForeignKey(Task.id)) > comment_given_by = Column('COMMENT_GIVEN_BY', VARCHAR2(250)) > task = relationship('Task', back_populates='comments', lazy='joined') > > Running the query without any attempt to limit columns works fine (ie. the > Classes/models seem OK) > query = session.query(Task) > > But now I tried .with_entities. > If you ONLY request actual Columns, and not relationships, it lists the > columns and tables in the select, but does not add the ON clause (cartesian > join) > If I request the relationship, it generates invalid SQL: > query = session.query(Task).with_entities(Task.id, Task.comments) > # SELECT > # "TASK"."ID", > # "TASK"."ID" = "COMMENT"."MSG_ID" AS comments > # FROM > # "TASK", > # "COMMENT" > > > So trying with .load_only: > No luck many ways, all failing... every random thing I try results in a > different exception - I feel like I'm getting farther and just guessing now
The documentation for what you are trying to do is at: https://docs.sqlalchemy.org/en/latest/orm/loading_columns.html#deferred-loading-with-multiple-entities When there are multiple entities, the Query never jumps between two entities without you specifying how you get there. in this case, the way you navigate between them is via Task.comments, so that navigation must be stated: session.query(Task).options(load_only(Task.id), defaultload(Task.comments).load_only(Comment.comment_given_by)) You in fact did pretty much the same query at the end of your attempts and I think you probably relied upon print(query) to determine if it was "working" or not, because your query with subqueryload() works, you just would not be able to see this by print(query) alone because subqueryload() emits a second query only when the objects are loaded. > > query = session.query(Task, Comment).options(load_only(Task.id, > Comment.comment_given_by)) > ---->sqlalchemy.exc.ArgumentError: Wildcard loader can only be used with > exactly one entity. Use Load(ent) to specify specific entities. load_only means, defer('*') + undefer(Task.id) + undefer(Comment.comment_given_by). But the wildcard doesn't know how to do Task, Comment together, you would need to say Load(Task).load_only(Task.id), Load(Comment).load_only(Comment.comment_given_by), but again this is not what you want because you are asking for a cartesian product in the above query. Error message is not great here because it comes from the defer('*') that you can't see happening here, so that should be improved. > query = session.query(Task).options(load_only('id', 'comments')) > ----> Exception: can't locate strategy for <class > 'sqlalchemy.orm.relationships.RelationshipProperty'> (('deferred', False), > ('instrument', True)) load_only() does not apply to relationships like "comments', only to columns. again not a great error message here, that could be improved. > > query = session.query(Task).options(load_only(Task.id, > Comment.comment_given_by)) > ----> sqlalchemy.exc.ArgumentError: Can't find property 'comment_given_by' on > any entity specified in this Query. Note the full path from root > (Mapper|Task|TASK) to target entity must be specified. Task has nothing to do with Comment unless you state this in terms of Task.comments, which is what "full path" means > > query = session.query(Task, Comment).options(load_only(Task.id, > Comment.comment_given_by)) > ----> sqlalchemy.exc.ArgumentError: Wildcard loader can only be used with > exactly one entity. Use Load(ent) to specify specific entities. you already tried this one above > > query = session.query(Task).options(load_only(Task.id, Task.comments)) > ----> Exception: can't locate strategy for <class > 'sqlalchemy.orm.relationships.RelationshipProperty'> (('deferred', False), > ('instrument', True)) this is the same thing as the other one you did, string attribute names or Object.attrname, same thing > > query = session.query(Task).options(load_only(Task.comments), > subqueryload("comments").load_only('comment_given_by')) > ----> No exception but only generates: "SELECT "TASK"."ID" FROM "TASK" I'm not sure you tested this correctly, this one actually works - the subqueryload("comments") cancels out the load_only(Task.comments) that would normally raise an error. Subqueryload is emitted as a second query, so if you just said print(query), you would not have seen it. Here is what it emits if I run on a model like this: s.query(A).options(load_only(A.bs), subqueryload(A.bs).load_only(B.data)).all() output: SELECT a.id AS a_id FROM a SELECT b.id AS b_id, b.data AS b_data, anon_1.a_id AS anon_1_a_id FROM (SELECT a.id AS a_id FROM a) AS anon_1 JOIN b ON anon_1.a_id = b.a_id ORDER BY anon_1.a_id > > -- > 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.