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 [email protected]
> <mailto:[email protected]>.
> To post to this group, send email to [email protected]
> <mailto:[email protected]>.
> 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 [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.