Skip to content

Commit

Permalink
Fixed #XXXXX -- Allowed callable values in through_defaults
Browse files Browse the repository at this point in the history
  • Loading branch information
bmispelon committed Nov 29, 2019
1 parent 39d680a commit e420bbc
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 1 deletion.
3 changes: 2 additions & 1 deletion django/db/models/fields/related_descriptors.py
Expand Up @@ -68,6 +68,7 @@ class Child(Model):
from django.db.models import Q, signals
from django.db.models.query import QuerySet
from django.db.models.query_utils import DeferredAttribute
from django.db.models.utils import resolve_callables
from django.utils.functional import cached_property


Expand Down Expand Up @@ -1113,7 +1114,7 @@ def _add_items(self, source_field_name, target_field_name, *objs, through_defaul
# source_field_name: the PK fieldname in join table for the source object
# target_field_name: the PK fieldname in join table for the target object
# *objs - objects to add. Either object instances, or primary keys of object instances.
through_defaults = through_defaults or {}
through_defaults = dict(resolve_callables(through_defaults or {}))

# If there aren't any objects, there is nothing to do.
if objs:
Expand Down
15 changes: 15 additions & 0 deletions docs/ref/models/relations.txt
Expand Up @@ -73,6 +73,11 @@ Related objects reference
:ref:`intermediate model <intermediary-manytomany>` instance(s), if
needed.

.. versionchanged:: 3.1

You can use callables as values in the ``through_defaults`` dictionary
and they will be evaluated just before adding the model.

.. method:: create(through_defaults=None, **kwargs)

Creates a new object, saves it and puts it in the related object set.
Expand Down Expand Up @@ -107,6 +112,11 @@ Related objects reference
:ref:`intermediate model <intermediary-manytomany>` instance, if
needed.

.. versionchanged:: 3.1

You can use callables as values in the ``through_defaults`` dictionary
and they will be evaluated just before saving the model.

.. method:: remove(*objs, bulk=True)

Removes the specified model objects from the related object set::
Expand Down Expand Up @@ -195,6 +205,11 @@ Related objects reference
:ref:`intermediate model <intermediary-manytomany>` instance(s), if
needed.

.. versionchanged:: 3.1

You can use callables as values in the ``through_defaults`` dictionary
and they will be evaluated just before saving the model.

.. note::

Note that ``add()``, ``create()``, ``remove()``, ``clear()``, and
Expand Down
5 changes: 5 additions & 0 deletions docs/releases/3.1.txt
Expand Up @@ -209,6 +209,11 @@ Models

* :attr:`.CheckConstraint.check` now supports boolean expressions.

* Callables can now be used with the ``through_defaults`` argument to
:meth:`RelatedManager.add() <django.db.models.fields.related.RelatedManager.add>`,
:meth:`~django.db.models.fields.related.RelatedManager.set`, and
:meth:`~django.db.models.fields.related.RelatedManager.create`.

Pagination
~~~~~~~~~~

Expand Down
5 changes: 5 additions & 0 deletions tests/m2m_through/tests.py
Expand Up @@ -62,6 +62,11 @@ def test_add_on_m2m_with_intermediate_model(self):
self.assertSequenceEqual(self.rock.members.all(), [self.bob])
self.assertEqual(self.rock.membership_set.get().invite_reason, 'He is good.')

def test_add_on_m2m_with_intermediate_model_callable_through_default(self):
self.rock.members.add(self.bob, through_defaults={'invite_reason': lambda: 'He is good.'})
self.assertSequenceEqual(self.rock.members.all(), [self.bob])
self.assertEqual(self.rock.membership_set.get().invite_reason, 'He is good.')

def test_add_on_m2m_with_intermediate_model_value_required(self):
self.rock.nodefaultsnonulls.add(self.jim, through_defaults={'nodefaultnonull': 1})
self.assertEqual(self.rock.testnodefaultsornulls_set.get().nodefaultnonull, 1)
Expand Down

0 comments on commit e420bbc

Please sign in to comment.