17

Is there a way to force an object mapped by sqlalchemy to be considered dirty? For example, given the context of sqlalchemy's Object Relational Tutorial the problem is demonstrated,

a=session.query(User).first()
a.__dict__['name']='eh'
session.dirty

yielding,

IdentitySet([])

i am looking for a way to force the user a into a dirty state.

This problem arises because the class that is mapped using sqlalchemy takes control of the attribute getter/setter methods, and this preventing sqlalchemy from registering changes.

alex
  • 2,968
  • 3
  • 23
  • 25

2 Answers2

29

I came across the same problem recently and it was not obvious.

Objects themselves are not dirty, but their attributes are. As SQLAlchemy will write back only changed attributes, not the whole object, as far as I know.

If you set an attribute using set_attribute and it is different from the original attribute data, SQLAlchemy founds out the object is dirty (TODO: I need details how it does the comparison):

   from sqlalchemy.orm.attributes import set_attribute
   set_attribute(obj, data_field_name, data)

If you want to mark the object dirty regardless of the original attribute value, no matter if it has changed or not, use flag_modified:

   from sqlalchemy.orm.attributes import flag_modified
   flag_modified(obj, data_field_name)
Mikko Ohtamaa
  • 82,057
  • 50
  • 264
  • 435
  • 1
    This answer seems to be risky however, if you mark attribute as modified but there won't be a state for it in state_dict it will cause KeyError. site-packages/sqlalchemy/orm/persistence.py", line 454, in _collect_update_commands value = state_dict[propkey] – Drachenfels Jul 22 '16 at 15:13
  • Thanks! Is it possible to change the primary key id as well using set attribute? – Pratik Khadloya Mar 06 '18 at 21:17
  • 1
    There is no need in using set_attribute(obj, data_field_name, data), just use setattr(obj, data_field_name, data) – Vladimir Osinskiy Feb 10 '23 at 03:43
7

The flag_modified approach works if one know that attribute have a value present. SQLAlchemy documentation states:

Mark an attribute on an instance as ‘modified’.

This sets the ‘modified’ flag on the instance and establishes an unconditional change event for the given attribute. The attribute must have a value present, else an InvalidRequestError is raised.

Starting with version 1.2, if one wants to mark an entire instance then flag_dirty is the solution:

Mark an instance as ‘dirty’ without any specific attribute mentioned.

FelixEnescu
  • 4,664
  • 2
  • 33
  • 34