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 [email protected] <javascript:>
> > <mailto:[email protected] <javascript:>>.
> > To post to this group, send email to [email protected]
> <javascript:>
> > <mailto:[email protected] <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 [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.