From 71ada3a8e689a883b5ffdeb1744ea16f176ab730 Mon Sep 17 00:00:00 2001 From: Loic Bistuer Date: Fri, 30 Jan 2015 01:15:27 +0700 Subject: Fixed #6707 -- Added RelatedManager.set() and made descriptors' __set__ use it. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks Anssi Kääriäinen, Carl Meyer, Collin Anderson, and Tim Graham for the reviews. --- docs/ref/models/relations.txt | 39 ++++++++++++++++++++++++++++++++------- docs/releases/1.9.txt | 24 +++++++++++++++++++++++- docs/topics/db/queries.txt | 3 +++ 3 files changed, 58 insertions(+), 8 deletions(-) (limited to 'docs') diff --git a/docs/ref/models/relations.txt b/docs/ref/models/relations.txt index 7f5eb18ea6..8c3b5a2f33 100644 --- a/docs/ref/models/relations.txt +++ b/docs/ref/models/relations.txt @@ -135,12 +135,31 @@ Related objects reference :class:`~django.db.models.ForeignKey`\s where ``null=True`` and it also accepts the ``bulk`` keyword argument. + .. method:: set(objs, clear=False) + + .. versionadded:: 1.9 + + Replace the set of related objects:: + + >>> new_list = [obj1, obj2, obj3] + >>> e.related_set.set(new_list) + + This method accepts a ``clear`` argument to control how to perform the + operation. If ``False`` (the default), the elements missing from the + new set are removed using ``remove()`` and only the new ones are added. + If ``clear=True``, the ``clear()`` method is called instead and the + whole set is added at once. + + Note that since ``set()`` is a compound operation, it is subject to + race conditions. For instance, new objects may be added to the database + in between the call to ``clear()`` and the call to ``add()``. + .. note:: - Note that ``add()``, ``create()``, ``remove()``, and ``clear()`` all - apply database changes immediately for all types of related fields. In - other words, there is no need to call ``save()`` on either end of the - relationship. + Note that ``add()``, ``create()``, ``remove()``, ``clear()``, and + ``set()`` all apply database changes immediately for all types of + related fields. In other words, there is no need to call ``save()`` + on either end of the relationship. Also, if you are using :ref:`an intermediate model ` for a many-to-many relationship, some of the @@ -158,6 +177,12 @@ new iterable of objects to it:: >>> e.related_set = new_list If the foreign key relationship has ``null=True``, then the related manager -will first call ``clear()`` to disassociate any existing objects in the related -set before adding the contents of ``new_list``. Otherwise the objects in -``new_list`` will be added to the existing related object set. +will first disassociate any existing objects in the related set before adding +the contents of ``new_list``. Otherwise the objects in ``new_list`` will be +added to the existing related object set. + +.. versionchanged:1.9 + + In earlier versions, direct assignment used to perform ``clear()`` followed + by ``add()``. It now performs a ``set()`` with the keyword argument + ``clear=False``. diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt index eee63c6f6d..d00bfef096 100644 --- a/docs/releases/1.9.txt +++ b/docs/releases/1.9.txt @@ -127,7 +127,10 @@ Management Commands Models ^^^^^^ -* ... +* Added the :meth:`RelatedManager.set() + ` method to the related + managers created by ``ForeignKey``, ``GenericForeignKey``, and + ``ManyToManyField``. Signals ^^^^^^^ @@ -192,6 +195,25 @@ used by the egg loader to detect if setuptools was installed. The ``is_usable`` attribute is now removed and the egg loader instead fails at runtime if setuptools is not installed. +Related set direct assignment +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:ref:`Direct assignment `) used to perform a ``clear()`` +followed by a call to ``add()``. This caused needlessly large data changes +and prevented using the :data:`~django.db.models.signals.m2m_changed` signal +to track individual changes in many-to-many relations. + +Direct assignment now relies on the the new +:meth:`django.db.models.fields.related.RelatedManager.set()` method on +related managers which by default only processes changes between the +existing related set and the one that's newly assigned. The previous behavior +can be restored by replacing direct assignment by a call to ``set()`` with +the keyword argument ``clear=True``. + +``ModelForm``, and therefore ``ModelAdmin``, internally rely on direct +assignment for many-to-many relations and as a consequence now use the new +behavior. + Miscellaneous ~~~~~~~~~~~~~ diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt index c616d02b33..34342ab3eb 100644 --- a/docs/topics/db/queries.txt +++ b/docs/topics/db/queries.txt @@ -1190,6 +1190,9 @@ be found in the :doc:`related objects reference `. ``clear()`` Removes all objects from the related object set. +``set(objs)`` + Replace the set of related objects. + To assign the members of a related set in one fell swoop, just assign to it from any iterable object. The iterable can contain object instances, or just a list of primary key values. For example:: -- cgit v1.3