From 85458e94e38c20e57939947ee515a1a53689659f Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Sat, 17 Aug 2019 15:30:29 +0200 Subject: [PATCH] Fixed #29260 -- Skipped an UPDATE when adding a model instance with primary key that has a default. --- django/db/models/base.py | 9 +++++++++ tests/basic/models.py | 6 ++++++ tests/basic/tests.py | 10 +++++++++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/django/db/models/base.py b/django/db/models/base.py index 91b4639524..ae27d3691a 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -15,6 +15,7 @@ from django.db import ( DEFAULT_DB_ALIAS, DJANGO_VERSION_PICKLE_KEY, DatabaseError, connection, connections, router, transaction, ) +from django.db.models import NOT_PROVIDED from django.db.models.constants import LOOKUP_SEP from django.db.models.constraints import CheckConstraint, UniqueConstraint from django.db.models.deletion import CASCADE, Collector @@ -842,6 +843,14 @@ class Model(metaclass=ModelBase): if not pk_set and (force_update or update_fields): raise ValueError("Cannot force an update in save() with no primary key.") updated = False + # Skip an UPDATE when adding an instance and primary key has a default. + if ( + not force_insert and + self._state.adding and + self._meta.pk.default and + self._meta.pk.default is not NOT_PROVIDED + ): + force_insert = True # If possible, try an UPDATE. If that doesn't update anything, do an INSERT. if pk_set and not force_insert: base_qs = cls._base_manager.using(using) diff --git a/tests/basic/models.py b/tests/basic/models.py index 40de6ae7de..7b2b1aee54 100644 --- a/tests/basic/models.py +++ b/tests/basic/models.py @@ -3,6 +3,8 @@ Bare-bones model This is a basic model with only two non-primary-key fields. """ +import uuid + from django.db import models @@ -40,3 +42,7 @@ class SelfRef(models.Model): # This method intentionally doesn't work for all cases - part # of the test for ticket #20278 return SelfRef.objects.get(selfref=self).pk + + +class PrimaryKeyWithDefault(models.Model): + uuid = models.UUIDField(primary_key=True, default=uuid.uuid4) diff --git a/tests/basic/tests.py b/tests/basic/tests.py index b29dda64f7..89f6048c96 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -10,7 +10,10 @@ from django.test import ( ) from django.utils.translation import gettext_lazy -from .models import Article, ArticleSelectOnSave, FeaturedArticle, SelfRef +from .models import ( + Article, ArticleSelectOnSave, FeaturedArticle, PrimaryKeyWithDefault, + SelfRef, +) class ModelInstanceCreationTests(TestCase): @@ -130,6 +133,11 @@ class ModelInstanceCreationTests(TestCase): # ... but there will often be more efficient ways if that is all you need: self.assertTrue(Article.objects.filter(id=a.id).exists()) + def test_save_primary_with_default(self): + # An UPDATE attempt is skipped when a primary key has default. + with self.assertNumQueries(1): + PrimaryKeyWithDefault().save() + class ModelTest(TestCase): def test_objects_attribute_is_only_available_on_the_class_itself(self):