[1.8.x] Fixed #23617 -- Added get_pk_value_on_save()
The method is mainly intended for use with UUIDField. For UUIDField we
want to call the field's default even when primary key value is
explicitly set to None to match the behavior of AutoField.
Thanks to Marc Tamlyn and Tim Graham for review.
Backport of 8adc59038c
from master
This commit is contained in:
parent
9ffe013caa
commit
43b0131fb5
|
@ -772,6 +772,9 @@ class Model(six.with_metaclass(ModelBase)):
|
||||||
if f.name in update_fields or f.attname in update_fields]
|
if f.name in update_fields or f.attname in update_fields]
|
||||||
|
|
||||||
pk_val = self._get_pk_val(meta)
|
pk_val = self._get_pk_val(meta)
|
||||||
|
if pk_val is None:
|
||||||
|
pk_val = meta.pk.get_pk_value_on_save(self)
|
||||||
|
setattr(self, meta.pk.attname, pk_val)
|
||||||
pk_set = pk_val is not None
|
pk_set = pk_val is not None
|
||||||
if not pk_set and (force_update or update_fields):
|
if not pk_set and (force_update or update_fields):
|
||||||
raise ValueError("Cannot force an update in save() with no primary key.")
|
raise ValueError("Cannot force an update in save() with no primary key.")
|
||||||
|
|
|
@ -506,6 +506,17 @@ class Field(RegisterLookupMixin):
|
||||||
return _load_field, (self.model._meta.app_label, self.model._meta.object_name,
|
return _load_field, (self.model._meta.app_label, self.model._meta.object_name,
|
||||||
self.name)
|
self.name)
|
||||||
|
|
||||||
|
def get_pk_value_on_save(self, instance):
|
||||||
|
"""
|
||||||
|
Hook to generate new PK values on save. This method is called when
|
||||||
|
saving instances with no primary key value set. If this method returns
|
||||||
|
something else than None, then the returned value is used when saving
|
||||||
|
the new instance.
|
||||||
|
"""
|
||||||
|
if self.default:
|
||||||
|
return self.get_default()
|
||||||
|
return None
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
"""
|
"""
|
||||||
Converts the input value into the expected Python data type, raising
|
Converts the input value into the expected Python data type, raising
|
||||||
|
|
|
@ -345,6 +345,11 @@ class QuerySet(object):
|
||||||
obj.save(force_insert=True, using=self.db)
|
obj.save(force_insert=True, using=self.db)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
def _populate_pk_values(self, objs):
|
||||||
|
for obj in objs:
|
||||||
|
if obj.pk is None:
|
||||||
|
obj.pk = obj._meta.pk.get_pk_value_on_save(obj)
|
||||||
|
|
||||||
def bulk_create(self, objs, batch_size=None):
|
def bulk_create(self, objs, batch_size=None):
|
||||||
"""
|
"""
|
||||||
Inserts each of the instances into the database. This does *not* call
|
Inserts each of the instances into the database. This does *not* call
|
||||||
|
@ -369,6 +374,8 @@ class QuerySet(object):
|
||||||
self._for_write = True
|
self._for_write = True
|
||||||
connection = connections[self.db]
|
connection = connections[self.db]
|
||||||
fields = self.model._meta.local_concrete_fields
|
fields = self.model._meta.local_concrete_fields
|
||||||
|
objs = list(objs)
|
||||||
|
self._populate_pk_values(objs)
|
||||||
with transaction.atomic(using=self.db, savepoint=False):
|
with transaction.atomic(using=self.db, savepoint=False):
|
||||||
if (connection.features.can_combine_inserts_with_and_without_auto_increment_pk
|
if (connection.features.can_combine_inserts_with_and_without_auto_increment_pk
|
||||||
and self.model._meta.has_auto_field):
|
and self.model._meta.has_auto_field):
|
||||||
|
|
|
@ -220,6 +220,15 @@ Note that ``lambda``\s cannot be used for field options like ``default``
|
||||||
because they cannot be :ref:`serialized by migrations <migration-serializing>`.
|
because they cannot be :ref:`serialized by migrations <migration-serializing>`.
|
||||||
See that documentation for other caveats.
|
See that documentation for other caveats.
|
||||||
|
|
||||||
|
The default value is used when new model instances are created and a value
|
||||||
|
isn't provided for the field. When the field is a primary key, the default is
|
||||||
|
also used when the field is set to ``None``.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.8
|
||||||
|
|
||||||
|
The default wasn't used for ``None`` primary key values in previous
|
||||||
|
versions.
|
||||||
|
|
||||||
``editable``
|
``editable``
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|
|
@ -525,6 +525,9 @@ Models
|
||||||
* You can now get the set of deferred fields for a model using
|
* You can now get the set of deferred fields for a model using
|
||||||
:meth:`Model.get_deferred_fields() <django.db.models.Model.get_deferred_fields>`.
|
:meth:`Model.get_deferred_fields() <django.db.models.Model.get_deferred_fields>`.
|
||||||
|
|
||||||
|
* Model field ``default``’s are now used when primary key field's are set to
|
||||||
|
``None``.
|
||||||
|
|
||||||
Signals
|
Signals
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -87,3 +87,20 @@ class TestAsPrimaryKey(TestCase):
|
||||||
PrimaryKeyUUIDModel.objects.create()
|
PrimaryKeyUUIDModel.objects.create()
|
||||||
loaded = PrimaryKeyUUIDModel.objects.get()
|
loaded = PrimaryKeyUUIDModel.objects.get()
|
||||||
self.assertIsInstance(loaded.pk, uuid.UUID)
|
self.assertIsInstance(loaded.pk, uuid.UUID)
|
||||||
|
|
||||||
|
def test_uuid_pk_on_save(self):
|
||||||
|
saved = PrimaryKeyUUIDModel.objects.create(id=None)
|
||||||
|
loaded = PrimaryKeyUUIDModel.objects.get()
|
||||||
|
self.assertIsNotNone(loaded.id, None)
|
||||||
|
self.assertEqual(loaded.id, saved.id)
|
||||||
|
|
||||||
|
def test_uuid_pk_on_bulk_create(self):
|
||||||
|
u1 = PrimaryKeyUUIDModel()
|
||||||
|
u2 = PrimaryKeyUUIDModel(id=None)
|
||||||
|
PrimaryKeyUUIDModel.objects.bulk_create([u1, u2])
|
||||||
|
# Check that the two objects were correctly created.
|
||||||
|
u1_found = PrimaryKeyUUIDModel.objects.filter(id=u1.id).exists()
|
||||||
|
u2_found = PrimaryKeyUUIDModel.objects.exclude(id=u1.id).exists()
|
||||||
|
self.assertTrue(u1_found)
|
||||||
|
self.assertTrue(u2_found)
|
||||||
|
self.assertEqual(PrimaryKeyUUIDModel.objects.count(), 2)
|
||||||
|
|
Loading…
Reference in New Issue