mirror of
https://github.com/django/django.git
synced 2024-12-23 01:25:58 +00:00
Fixed #26638 -- Allowed callable arguments for QuerySet.get_or_create()/update_or_create() defaults.
This commit is contained in:
parent
44c7e5d374
commit
9899347641
@ -500,6 +500,7 @@ class QuerySet(object):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
with transaction.atomic(using=self.db):
|
with transaction.atomic(using=self.db):
|
||||||
|
params = {k: v() if callable(v) else v for k, v in params.items()}
|
||||||
obj = self.create(**params)
|
obj = self.create(**params)
|
||||||
return obj, True
|
return obj, True
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
|
@ -1687,18 +1687,18 @@ tuple of the new object and ``True``. The new object will be created roughly
|
|||||||
according to this algorithm::
|
according to this algorithm::
|
||||||
|
|
||||||
params = {k: v for k, v in kwargs.items() if '__' not in k}
|
params = {k: v for k, v in kwargs.items() if '__' not in k}
|
||||||
params.update(defaults)
|
params.update(({k: v() if callable(v) else v for k, v in defaults.items()})
|
||||||
obj = self.model(**params)
|
obj = self.model(**params)
|
||||||
obj.save()
|
obj.save()
|
||||||
|
|
||||||
In English, that means start with any non-``'defaults'`` keyword argument that
|
In English, that means start with any non-``'defaults'`` keyword argument that
|
||||||
doesn't contain a double underscore (which would indicate a non-exact lookup).
|
doesn't contain a double underscore (which would indicate a non-exact lookup).
|
||||||
Then add the contents of ``defaults``, overriding any keys if necessary, and
|
Then add the contents of ``defaults``, overriding any keys if necessary, and
|
||||||
use the result as the keyword arguments to the model class. As hinted at
|
use the result as the keyword arguments to the model class. If there are any
|
||||||
above, this is a simplification of the algorithm that is used, but it contains
|
callables in ``defaults``, evaluate them. As hinted at above, this is a
|
||||||
all the pertinent details. The internal implementation has some more
|
simplification of the algorithm that is used, but it contains all the pertinent
|
||||||
error-checking than this and handles some extra edge-conditions; if you're
|
details. The internal implementation has some more error-checking than this and
|
||||||
interested, read the code.
|
handles some extra edge-conditions; if you're interested, read the code.
|
||||||
|
|
||||||
If you have a field named ``defaults`` and want to use it as an exact lookup in
|
If you have a field named ``defaults`` and want to use it as an exact lookup in
|
||||||
``get_or_create()``, just use ``'defaults__exact'``, like so::
|
``get_or_create()``, just use ``'defaults__exact'``, like so::
|
||||||
@ -1764,6 +1764,10 @@ whenever a request to a page has a side effect on your data. For more, see
|
|||||||
chapter because it isn't related to that book, but it can't create it either
|
chapter because it isn't related to that book, but it can't create it either
|
||||||
because ``title`` field should be unique.
|
because ``title`` field should be unique.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.11
|
||||||
|
|
||||||
|
Added support for callable values in ``defaults``.
|
||||||
|
|
||||||
``update_or_create()``
|
``update_or_create()``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@ -1771,7 +1775,7 @@ whenever a request to a page has a side effect on your data. For more, see
|
|||||||
|
|
||||||
A convenience method for updating an object with the given ``kwargs``, creating
|
A convenience method for updating an object with the given ``kwargs``, creating
|
||||||
a new one if necessary. The ``defaults`` is a dictionary of (field, value)
|
a new one if necessary. The ``defaults`` is a dictionary of (field, value)
|
||||||
pairs used to update the object.
|
pairs used to update the object. The values in ``defaults`` can be callables.
|
||||||
|
|
||||||
Returns a tuple of ``(object, created)``, where ``object`` is the created or
|
Returns a tuple of ``(object, created)``, where ``object`` is the created or
|
||||||
updated object and ``created`` is a boolean specifying whether a new object was
|
updated object and ``created`` is a boolean specifying whether a new object was
|
||||||
@ -1806,6 +1810,10 @@ As described above in :meth:`get_or_create`, this method is prone to a
|
|||||||
race-condition which can result in multiple rows being inserted simultaneously
|
race-condition which can result in multiple rows being inserted simultaneously
|
||||||
if uniqueness is not enforced at the database level.
|
if uniqueness is not enforced at the database level.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.11
|
||||||
|
|
||||||
|
Added support for callable values in ``defaults``.
|
||||||
|
|
||||||
``bulk_create()``
|
``bulk_create()``
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -175,7 +175,10 @@ Migrations
|
|||||||
Models
|
Models
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
|
||||||
* ...
|
* Added support for callable values in the ``defaults`` argument of
|
||||||
|
:meth:`QuerySet.update_or_create()
|
||||||
|
<django.db.models.query.QuerySet.update_or_create>` and
|
||||||
|
:meth:`~django.db.models.query.QuerySet.get_or_create`.
|
||||||
|
|
||||||
Requests and Responses
|
Requests and Responses
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -146,6 +146,25 @@ class GetOrCreateTests(TestCase):
|
|||||||
self.assertFalse(created)
|
self.assertFalse(created)
|
||||||
self.assertEqual(obj, obj2)
|
self.assertEqual(obj, obj2)
|
||||||
|
|
||||||
|
def test_callable_defaults(self):
|
||||||
|
"""
|
||||||
|
Callables in `defaults` are evaluated if the instance is created.
|
||||||
|
"""
|
||||||
|
obj, created = Person.objects.get_or_create(
|
||||||
|
first_name="George",
|
||||||
|
defaults={"last_name": "Harrison", "birthday": lambda: date(1943, 2, 25)},
|
||||||
|
)
|
||||||
|
self.assertTrue(created)
|
||||||
|
self.assertEqual(date(1943, 2, 25), obj.birthday)
|
||||||
|
|
||||||
|
def test_callable_defaults_not_called(self):
|
||||||
|
def raise_exception():
|
||||||
|
raise AssertionError
|
||||||
|
obj, created = Person.objects.get_or_create(
|
||||||
|
first_name="John", last_name="Lennon",
|
||||||
|
defaults={"birthday": lambda: raise_exception()},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class GetOrCreateTestsWithManualPKs(TestCase):
|
class GetOrCreateTestsWithManualPKs(TestCase):
|
||||||
|
|
||||||
@ -387,3 +406,10 @@ class UpdateOrCreateTests(TestCase):
|
|||||||
)
|
)
|
||||||
self.assertFalse(created)
|
self.assertFalse(created)
|
||||||
self.assertEqual(obj.defaults, 'another testing')
|
self.assertEqual(obj.defaults, 'another testing')
|
||||||
|
|
||||||
|
def test_update_callable_default(self):
|
||||||
|
obj, created = Person.objects.update_or_create(
|
||||||
|
first_name='George', last_name='Harrison',
|
||||||
|
defaults={'birthday': lambda: date(1943, 2, 25)},
|
||||||
|
)
|
||||||
|
self.assertEqual(obj.birthday, date(1943, 2, 25))
|
||||||
|
Loading…
Reference in New Issue
Block a user