When customers call our JSON:API API, they can use an "include" parameter to specify related objects to be appended to the response.
However, requests to include are not always satisfiable (e.g. if job.supervisor=null, include=supervisor is ignored).
In order to prevent Marshmallow from trying to load nonexistent related objects to append to our API responses, we need to tell it when to ignore a relationship attribute, such as via setting job.supervisor=marshmallow.missing (if it sees job.supervisor=None, it will attempt (and fail) to load the null supervisor object, so we cannot just leave it as-is).
Unfortunately, this causes problems as SQLAlchemy attempts to handle the new value:
Error
Traceback (most recent call last):
File "/Users/lucadou/IdeaProjects/person-api/api/unit_tests/test_person_service.py", line 601, in test_get_people_include_job
response = self.person_service.get_people(QueryParameters({"include": "jobs"}))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/lucadou/IdeaProjects/person-api/api/src/person_service.py", line 61, in get_people
response = person_schema.dump(people, many=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/lucadou/IdeaProjects/person-api/api/venv/lib/python3.11/site-packages/marshmallow/schema.py", line 557, in dump
result = self._serialize(processed_obj, many=many)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/lucadou/IdeaProjects/person-api/api/venv/lib/python3.11/site-packages/marshmallow/schema.py", line 519, in _serialize
return [
^
File "/Users/lucadou/IdeaProjects/person-api/api/venv/lib/python3.11/site-packages/marshmallow/schema.py", line 520, in <listcomp>
self._serialize(d, many=False)
File "/Users/lucadou/IdeaProjects/person-api/api/venv/lib/python3.11/site-packages/marshmallow/schema.py", line 525, in _serialize
value = field_obj.serialize(attr_name, obj, accessor=self.get_attribute)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/lucadou/IdeaProjects/person-api/api/venv/lib/python3.11/site-packages/marshmallow_jsonapi/fields.py", line 248, in serialize
return super().serialize(attr, obj, accessor)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/lucadou/IdeaProjects/person-api/api/venv/lib/python3.11/site-packages/marshmallow/fields.py", line 344, in serialize
return self._serialize(value, attr, obj, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/lucadou/IdeaProjects/person-api/api/venv/lib/python3.11/site-packages/marshmallow_jsonapi/fields.py", line 274, in _serialize
self._serialize_included(item)
File "/Users/lucadou/IdeaProjects/person-api/api/venv/lib/python3.11/site-packages/marshmallow_jsonapi/fields.py", line 280, in _serialize_included
result = self.schema.dump(value)
^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/lucadou/IdeaProjects/person-api/api/venv/lib/python3.11/site-packages/marshmallow/schema.py", line 551, in dump
processed_obj = self._invoke_dump_processors(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/lucadou/IdeaProjects/person-api/api/venv/lib/python3.11/site-packages/marshmallow/schema.py", line 1068, in _invoke_dump_processors
data = self._invoke_processors(
^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/lucadou/IdeaProjects/person-api/api/venv/lib/python3.11/site-packages/marshmallow/schema.py", line 1225, in _invoke_processors
data = processor(data, many=many, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/lucadou/IdeaProjects/person-api/api/src/model/schema/job_schema.py", line 62, in set_null_supervisor
job.supervisor = missing
^^^^^^^^^^^^^^
File "/Users/lucadou/IdeaProjects/person-api/api/venv/lib/python3.11/site-packages/sqlalchemy/orm/attributes.py", line 536, in __set__
self.impl.set(
File "/Users/lucadou/IdeaProjects/person-api/api/venv/lib/python3.11/site-packages/sqlalchemy/orm/attributes.py", line 1466, in set
value = self.fire_replace_event(state, dict_, value, old, initiator)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/lucadou/IdeaProjects/person-api/api/venv/lib/python3.11/site-packages/sqlalchemy/orm/attributes.py", line 1505, in fire_replace_event
value = fn(
^^^
File "/Users/lucadou/IdeaProjects/person-api/api/venv/lib/python3.11/site-packages/sqlalchemy/orm/attributes.py", line 2167, in emit_backref_from_scalar_set_event
instance_state(child),
^^^^^^^^^^^^^^^^^^^^^
AttributeError: '_Missing' object has no attribute '_sa_instance_state'
Is there any way to temporarily disable ORM event listeners when we mutate objects and have no intention of saving the changes/do not intend for the ORM to act on them?