Search code examples
pythonormsqlalchemyflask-sqlalchemybulkupdate

SQLAlchemy bulk update query fails with AttributeError


Following are model and relationship configurations I have, (not a practical example)

class MyUser(Base):
    __tablename__ = 'my_user'
    id = Column(Integer, primary_key=True)
    name = Column(String)


class Address(Base):
    __tablename__ = 'address'
    id = Column(Integer, primary_key=True)
    email = Column(String)
    my_user = relationship("MyUser", backref="addresses", cascade='save-update, merge')
    _my_user_id = Column('my_user_id', Integer, ForeignKey('my_user.id'), nullable=True)


>>> user = db_session.query(User).get(6)
>>> add = models.Address(email='[email protected]', my_user=user)
>>> db_session.add(add)
>>> db_session.commit()
>>> db_session.query(models.Address).filter_by(my_user=user).update(values=dict(email='[email protected]'))
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<hidden>/lib64/python3.6/site-packages/sqlalchemy/orm/query.py", line 3345, in update
        update_op.exec_()
      File "<hidden>/lib64/python3.6/site-packages/sqlalchemy/orm/persistence.py", line 1178, in exec_
        self._do_pre_synchronize()
      File "<hidden>/lib64/python3.6/site-packages/sqlalchemy/orm/persistence.py", line 1254, in _do_pre_synchronize
        query.session.identity_map.items()
      File "<hidden>/lib64/python3.6/site-packages/sqlalchemy/orm/persistence.py", line 1256, in <listcomp>
        eval_condition(obj)]
      File "<hidden>/lib64/python3.6/site-packages/sqlalchemy/orm/evaluator.py", line 109, in evaluate
        right_val = eval_right(obj)
      File "<hidden>/lib64/python3.6/site-packages/sqlalchemy/orm/evaluator.py", line 65, in <lambda>
        return lambda obj: get_corresponding_attr(obj)
    AttributeError: 'Address' object has no attribute 'my_user_id'

This works fine when,

>>> db_session.query(models.Address).filter_by(my_user=user).update(values=dict(email='[email protected]'), synchronize_session=False)
>>> db_session.query(models.Address).filter_by(_my_user_id=user.id).update(values=dict(email='[email protected]'))

Even it works after session close,

>> db_session.close()
>>> db_session.query(models.Address).filter_by(my_user=user).update(values=dict(email='[email protected]'))

Is there something wrong with update query? Why it works in other cases?


Solution

  • This is issue 3366 in SQLAlchemy < 1.2.x, fixed in the 1.2.x series:

    The “evaluate” strategy used by Query.update() and Query.delete() can now accommodate a simple object comparison from a many-to-one relationship to an instance, when the attribute names of the primary key / foreign key columns don’t match the actual names of the columns. Previously this would do a simple name-based match and fail with an AttributeError.

    The simplest solution is to upgrade, or avoid using the "evaluate" strategy for synchronizing the session when performing bulk operations:

    db_session.query(models.Address).\
        filter_by(my_user=user).\
        update(values=dict(email='[email protected]'),
               synchronize_session=False)
    # ... or 'fetch'