Thanks Michael.

On Saturday, 21 November 2015 22:35:14 UTC, Michael Bayer wrote:
>
>
>
> On 11/20/2015 12:20 PM, Martin Pengelly-Phillips wrote: 
> > Hi there, 
> > 
> > Using SQLAlchemy 1.0.9 
> > 
> > I am dealing with some legacy code and came across the following issue. 
> > It appears that in a polymorphic relationship SQLAlchemy is not able to 
> > correctly determine what to load for a relationship when using a 
> > subselect (due to limit on query) and child class has a different field 
> > name for the primary key. 
> > 
> > Here is a reproducible test case to show the issue better: 
> > 
> > | 
> > fromuuid importuuid1 asuuid 
> > 
> > importsqlalchemy 
> > importsqlalchemy.orm 
> > fromsqlalchemy import( 
> >     Column,Unicode,ForeignKey,CHAR,Boolean,UniqueConstraint 
> > ) 
> > fromsqlalchemy.orm importrelationship,backref,synonym 
> > fromsqlalchemy.ext.declarative importdeclarative_base,declared_attr 
> > 
> > 
> > Base=declarative_base() 
> > 
> > 
> > classContext(Base): 
> >     '''Represent a context.''' 
> > 
> >     __tablename__ ='context' 
> > 
> >     context_type =Column(Unicode(32),nullable=False) 
> > 
> >     __mapper_args__ ={ 
> >         'polymorphic_on':context_type, 
> >         'polymorphic_identity':'context' 
> >     } 
> > 
> >     id =Column(CHAR(36),primary_key=True,default=lambda:str(uuid())) 
> > 
> > 
> > classTask(Context): 
> >     '''Represent a task.''' 
> > 
> >     __tablename__ ='task' 
> > 
> >     __mapper_args__ ={ 
> >         'polymorphic_identity':'task' 
> >     } 
> > 
> >     # Change this and references to it to 'id' to fix issue. 
> >     task_id =Column( 
> >         CHAR(36), 
> >         ForeignKey('context.id'), 
> >         primary_key=True 
> >     ) 
> > 
> >     scopes =relationship('Scope',secondary='task_scope') 
> > 
> > 
> > classScope(Base): 
> >     '''Represent a Scope.''' 
> > 
> >     __tablename__ ='scope' 
> > 
> >     id =Column(CHAR(36),primary_key=True,default=lambda:str(uuid())) 
> > 
> > 
> > classTaskScope(Base): 
> >     '''Represent a relation between a scope and a task.''' 
> > 
> >     __tablename__ ='task_scope' 
> > 
> >     id =Column(CHAR(36),primary_key=True,default=lambda:str(uuid())) 
> > 
> >     scope_id =Column(CHAR(36),ForeignKey(Scope.id),nullable=False) 
> > 
> >     task_id =Column(CHAR(36),ForeignKey(Task.task_id),nullable=False) 
> > 
> > 
> > defmain(): 
> >     '''Execute test.''' 
> >     engine =sqlalchemy.create_engine('sqlite://') 
> >     Base.metadata.create_all(engine) 
> >     Session=sqlalchemy.orm.sessionmaker(bind=engine) 
> > 
> >     session =Session() 
> > 
> >     task =Task() 
> >     scope =Scope() 
> > 
> >     session.add(task) 
> >     session.add(scope) 
> >     session.commit() 
> > 
> >     link =TaskScope(scope_id=scope.id,task_id=task.task_id) 
> >     session.add(link) 
> >     session.commit() 
> > 
> >     query =session.query(Task).options( 
> >         sqlalchemy.orm.load_only('context_type'), 
> >         sqlalchemy.orm.joinedload('scopes').load_only() 
> >     ).limit(10) 
> >     printquery 
> > 
> >     results =query.all() 
> >     printresults 
> > 
> > 
> > if__name__ =='__main__': 
> >     main() 
> > | 
> > 
> > Running the above gives: 
> > 
> > | 
> > sqlalchemy.exc.OperationalError:(sqlite3.OperationalError)nosuch 
> > column:task.task_id [SQL:u'SELECT anon_1.context_context_type AS 
> > anon_1_context_context_type, anon_1.context_id AS anon_1_context_id, 
> > anon_2.scope_1_id AS scope_1_id \nFROM (SELECT context.context_type AS 
> > context_context_type, context.id AS context_id \nFROM context JOIN task 
> > ON context.id = task.task_id\n LIMIT ? OFFSET ?) AS anon_1 LEFT OUTER 
> > JOIN (SELECT task_scope_1.id AS task_scope_1_id, task_scope_1.scope_id 
> > AS task_scope_1_scope_id, task_scope_1.task_id AS task_scope_1_task_id, 
> > scope_1.id AS scope_1_id \nFROM task_scope AS task_scope_1 JOIN scope 
> AS 
> > scope_1 ON scope_1.id = task_scope_1.scope_id) AS anon_2 ON 
> task.task_id 
> > = anon_2.task_scope_1_task_id'][parameters:(10,0)] 
> > | 
> > 
> > 
> > If you change 'task_id' to 'id' (and references to it) then the query 
> > will now work correctly. 
>
> this is a bug and requires five separate conditions to all be in place 
> at the same time in order to occur.  This is issue 
>
> https://bitbucket.org/zzzeek/sqlalchemy/issues/3592/logic-to-ensure-parent-cols-present-in
>  
> fixed in 60c36ca8418cec18073 and 7998f15b1687a0bd0b3 for rel_1_0.   For 
> workaround, don't exclude the attribute that's needed for the 
> joinedload(), eg: 
>
>     query = session.query(Task).options( 
>         sqlalchemy.orm.load_only('context_type', 'task_id'), 
>         sqlalchemy.orm.joinedload('scopes').load_only() 
>     ).limit(10) 
>
>
>
>
>
> > 
> > As I mentioned this is legacy code so there are many references to 
> > task_id throughout the code base unfortunately. I have found a 
> > workaround by using a synonym, but wanted to report the issue to see if 
> > the failure is expected and there is a requirement for the primary key 
> > field names to match or not. 
> > 
> > cheers, 
> > 
> > 
> > Martin 
> > 
> > -- 
> > 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 http://groups.google.com/group/sqlalchemy. 
> > For more options, visit https://groups.google.com/d/optout. 
>

-- 
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 http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to