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+unsubscr...@googlegroups.com
> <mailto:sqlalchemy+unsubscr...@googlegroups.com>.
> To post to this group, send email to sqlalchemy@googlegroups.com
> <mailto:sqlalchemy@googlegroups.com>.
> 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