diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-08-10 10:53:11 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-08-16 20:05:32 -0400 |
| commit | 6cef8526226ab6033dfef1f793be87bff2160c04 (patch) | |
| tree | 693a5b244cbb6f02a78f9d1249e6bd58f90f8bfc /lib/sqlalchemy/orm/events.py | |
| parent | a134ec1760df6295d537ff63df7aee83d957bf6a (diff) | |
| download | sqlalchemy-6cef8526226ab6033dfef1f793be87bff2160c04.tar.gz | |
Propagate key for collection events
Added new parameter :paramref:`_orm.AttributeEvents.include_key`, which
will include the dictionary or list key for operations such as
``__setitem__()`` (e.g. ``obj[key] = value``) and ``__delitem__()`` (e.g.
``del obj[key]``), using a new keyword parameter "key" or "keys", depending
on event, e.g. :paramref:`_orm.AttributeEvents.append.key`,
:paramref:`_orm.AttributeEvents.bulk_replace.keys`. This allows event
handlers to take into account the key that was passed to the operation and
is of particular importance for dictionary operations working with
:class:`_orm.MappedCollection`.
Fixes: #8375
Change-Id: Icc472f7c28848f94e15c94a399cc13a88782e1e4
Diffstat (limited to 'lib/sqlalchemy/orm/events.py')
| -rw-r--r-- | lib/sqlalchemy/orm/events.py | 71 |
1 files changed, 63 insertions, 8 deletions
diff --git a/lib/sqlalchemy/orm/events.py b/lib/sqlalchemy/orm/events.py index 680e49981..c17ea1abe 100644 --- a/lib/sqlalchemy/orm/events.py +++ b/lib/sqlalchemy/orm/events.py @@ -22,6 +22,7 @@ from . import interfaces from . import mapperlib from .attributes import QueryableAttribute from .base import _mapper_or_none +from .base import NO_KEY from .query import Query from .scoping import scoped_session from .session import Session @@ -2288,6 +2289,7 @@ class AttributeEvents(event.Events): raw=False, retval=False, propagate=False, + include_key=False, ): target, fn = event_key.dispatch_target, event_key._listen_fn @@ -2295,9 +2297,9 @@ class AttributeEvents(event.Events): if active_history: target.dispatch._active_history = True - if not raw or not retval: + if not raw or not retval or not include_key: - def wrap(target, *arg): + def wrap(target, *arg, **kw): if not raw: target = target.obj() if not retval: @@ -2305,10 +2307,16 @@ class AttributeEvents(event.Events): value = arg[0] else: value = None - fn(target, *arg) + if include_key: + fn(target, *arg, **kw) + else: + fn(target, *arg) return value else: - return fn(target, *arg) + if include_key: + return fn(target, *arg, **kw) + else: + return fn(target, *arg) event_key = event_key.with_wrapper(wrap) @@ -2324,7 +2332,7 @@ class AttributeEvents(event.Events): if active_history: mgr[target.key].dispatch._active_history = True - def append(self, target, value, initiator): + def append(self, target, value, initiator, *, key=NO_KEY): """Receive a collection append event. The append event is invoked for each element as it is appended @@ -2343,6 +2351,19 @@ class AttributeEvents(event.Events): from its original value by backref handlers in order to control chained event propagation, as well as be inspected for information about the source of the event. + :param key: When the event is established using the + :paramref:`.AttributeEvents.include_key` parameter set to + True, this will be the key used in the operation, such as + ``collection[some_key_or_index] = value``. + The parameter is not passed + to the event at all if the the + :paramref:`.AttributeEvents.include_key` + was not used to set up the event; this is to allow backwards + compatibility with existing event handlers that don't include the + ``key`` parameter. + + .. versionadded:: 2.0 + :return: if the event was registered with ``retval=True``, the given value, or a new effective value, should be returned. @@ -2355,7 +2376,7 @@ class AttributeEvents(event.Events): """ - def append_wo_mutation(self, target, value, initiator): + def append_wo_mutation(self, target, value, initiator, *, key=NO_KEY): """Receive a collection append event where the collection was not actually mutated. @@ -2378,6 +2399,18 @@ class AttributeEvents(event.Events): from its original value by backref handlers in order to control chained event propagation, as well as be inspected for information about the source of the event. + :param key: When the event is established using the + :paramref:`.AttributeEvents.include_key` parameter set to + True, this will be the key used in the operation, such as + ``collection[some_key_or_index] = value``. + The parameter is not passed + to the event at all if the the + :paramref:`.AttributeEvents.include_key` + was not used to set up the event; this is to allow backwards + compatibility with existing event handlers that don't include the + ``key`` parameter. + + .. versionadded:: 2.0 :return: No return value is defined for this event. @@ -2385,7 +2418,7 @@ class AttributeEvents(event.Events): """ - def bulk_replace(self, target, values, initiator): + def bulk_replace(self, target, values, initiator, *, keys=None): """Receive a collection 'bulk replace' event. This event is invoked for a sequence of values as they are incoming @@ -2428,6 +2461,17 @@ class AttributeEvents(event.Events): handler can modify this list in place. :param initiator: An instance of :class:`.attributes.Event` representing the initiation of the event. + :param keys: When the event is established using the + :paramref:`.AttributeEvents.include_key` parameter set to + True, this will be the sequence of keys used in the operation, + typically only for a dictionary update. The parameter is not passed + to the event at all if the the + :paramref:`.AttributeEvents.include_key` + was not used to set up the event; this is to allow backwards + compatibility with existing event handlers that don't include the + ``key`` parameter. + + .. versionadded:: 2.0 .. seealso:: @@ -2437,7 +2481,7 @@ class AttributeEvents(event.Events): """ - def remove(self, target, value, initiator): + def remove(self, target, value, initiator, *, key=NO_KEY): """Receive a collection remove event. :param target: the object instance receiving the event. @@ -2453,6 +2497,17 @@ class AttributeEvents(event.Events): passed as a :class:`.attributes.Event` object, and may be modified by backref handlers within a chain of backref-linked events. + :param key: When the event is established using the + :paramref:`.AttributeEvents.include_key` parameter set to + True, this will be the key used in the operation, such as + ``del collection[some_key_or_index]``. The parameter is not passed + to the event at all if the the + :paramref:`.AttributeEvents.include_key` + was not used to set up the event; this is to allow backwards + compatibility with existing event handlers that don't include the + ``key`` parameter. + + .. versionadded:: 2.0 :return: No return value is defined for this event. |
