From: Mike Bayer Date: Tue, 30 Aug 2022 13:50:03 +0000 (-0400) Subject: apply consistent ORM mutable notes for all mutable SQL types X-Git-Tag: rel_2_0_0b1~84 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2f146b172ad228e40f1e8d5f1d2abc888ae5e669;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git apply consistent ORM mutable notes for all mutable SQL types in https://github.com/sqlalchemy/sqlalchemy/discussions/8447 I was surprised that we didnt have any notes about using Mutable for ARRAY classes, since we have them for HSTORE and JSON. Add a consistent topic box for these so we have something to point towards. Change-Id: Idfa1b2cbee67024545f4fa299e4c875075ec7d3f --- diff --git a/lib/sqlalchemy/dialects/postgresql/array.py b/lib/sqlalchemy/dialects/postgresql/array.py index 515eb2d150..3132e875e6 100644 --- a/lib/sqlalchemy/dialects/postgresql/array.py +++ b/lib/sqlalchemy/dialects/postgresql/array.py @@ -201,6 +201,31 @@ class ARRAY(sqltypes.ARRAY): conjunction with the :class:`.ENUM` type. For a workaround, see the special type at :ref:`postgresql_array_of_enum`. + .. container:: topic + + **Detecting Changes in ARRAY columns when using the ORM** + + The :class:`_postgresql.ARRAY` type, when used with the SQLAlchemy ORM, + does not detect in-place mutations to the array. In order to detect + these, the :mod:`sqlalchemy.ext.mutable` extension must be used, using + the :class:`.MutableList` class:: + + from sqlalchemy.dialects.postgresql import ARRAY + from sqlalchemy.ext.mutable import MutableList + + class SomeOrmClass(Base): + # ... + + data = Column(MutableList.as_mutable(ARRAY(Integer))) + + This extension will allow "in-place" changes such to the array + such as ``.append()`` to produce events which will be detected by the + unit of work. Note that changes to elements **inside** the array, + including subarrays that are mutated in place, are **not** detected. + + Alternatively, assigning a new array value to an ORM element that + replaces the old one will always trigger a change event. + .. seealso:: :class:`_types.ARRAY` - base array type diff --git a/lib/sqlalchemy/dialects/postgresql/hstore.py b/lib/sqlalchemy/dialects/postgresql/hstore.py index 43042b29f5..7a2dff7ea9 100644 --- a/lib/sqlalchemy/dialects/postgresql/hstore.py +++ b/lib/sqlalchemy/dialects/postgresql/hstore.py @@ -97,34 +97,38 @@ class HSTORE(sqltypes.Indexable, sqltypes.Concatenable, sqltypes.TypeEngine): For a full list of special methods see :class:`.HSTORE.comparator_factory`. - For usage with the SQLAlchemy ORM, it may be desirable to combine - the usage of :class:`.HSTORE` with :class:`.MutableDict` dictionary - now part of the :mod:`sqlalchemy.ext.mutable` - extension. This extension will allow "in-place" changes to the - dictionary, e.g. addition of new keys or replacement/removal of existing - keys to/from the current dictionary, to produce events which will be - detected by the unit of work:: + .. container:: topic - from sqlalchemy.ext.mutable import MutableDict + **Detecting Changes in HSTORE columns when using the ORM** - class MyClass(Base): - __tablename__ = 'data_table' + For usage with the SQLAlchemy ORM, it may be desirable to combine the + usage of :class:`.HSTORE` with :class:`.MutableDict` dictionary now + part of the :mod:`sqlalchemy.ext.mutable` extension. This extension + will allow "in-place" changes to the dictionary, e.g. addition of new + keys or replacement/removal of existing keys to/from the current + dictionary, to produce events which will be detected by the unit of + work:: - id = Column(Integer, primary_key=True) - data = Column(MutableDict.as_mutable(HSTORE)) + from sqlalchemy.ext.mutable import MutableDict - my_object = session.query(MyClass).one() + class MyClass(Base): + __tablename__ = 'data_table' - # in-place mutation, requires Mutable extension - # in order for the ORM to detect - my_object.data['some_key'] = 'some value' + id = Column(Integer, primary_key=True) + data = Column(MutableDict.as_mutable(HSTORE)) - session.commit() + my_object = session.query(MyClass).one() - When the :mod:`sqlalchemy.ext.mutable` extension is not used, the ORM - will not be alerted to any changes to the contents of an existing - dictionary, unless that dictionary value is re-assigned to the - HSTORE-attribute itself, thus generating a change event. + # in-place mutation, requires Mutable extension + # in order for the ORM to detect + my_object.data['some_key'] = 'some value' + + session.commit() + + When the :mod:`sqlalchemy.ext.mutable` extension is not used, the ORM + will not be alerted to any changes to the contents of an existing + dictionary, unless that dictionary value is re-assigned to the + HSTORE-attribute itself, thus generating a change event. .. seealso:: diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index 99afabc61e..fd52ec6ea1 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -2204,11 +2204,15 @@ class JSON(Indexable, TypeEngine[Any]): The :class:`_types.JSON` type, when used with the SQLAlchemy ORM, does not detect in-place mutations to the structure. In order to detect these, the - :mod:`sqlalchemy.ext.mutable` extension must be used. This extension will + :mod:`sqlalchemy.ext.mutable` extension must be used, most typically + using the :class:`.MutableDict` class. This extension will allow "in-place" changes to the datastructure to produce events which will be detected by the unit of work. See the example at :class:`.HSTORE` for a simple example involving a dictionary. + Alternatively, assigning a JSON structure to an ORM element that + replaces the old one will always trigger a change event. + **Support for JSON null vs. SQL NULL** When working with NULL values, the :class:`_types.JSON` type recommends the @@ -2735,6 +2739,31 @@ class ARRAY( :meth:`.types.ARRAY.Comparator.all`. The PostgreSQL-specific version of :class:`_types.ARRAY` also provides additional operators. + .. container:: topic + + **Detecting Changes in ARRAY columns when using the ORM** + + The :class:`_sqltypes.ARRAY` type, when used with the SQLAlchemy ORM, + does not detect in-place mutations to the array. In order to detect + these, the :mod:`sqlalchemy.ext.mutable` extension must be used, using + the :class:`.MutableList` class:: + + from sqlalchemy import ARRAY + from sqlalchemy.ext.mutable import MutableList + + class SomeOrmClass(Base): + # ... + + data = Column(MutableList.as_mutable(ARRAY(Integer))) + + This extension will allow "in-place" changes such to the array + such as ``.append()`` to produce events which will be detected by the + unit of work. Note that changes to elements **inside** the array, + including subarrays that are mutated in place, are **not** detected. + + Alternatively, assigning a new array value to an ORM element that + replaces the old one will always trigger a change event. + .. versionadded:: 1.1.0 .. seealso::