Opened 19 months ago

Last modified 15 months ago

#28715 new Cleanup/optimization

Prevent a migration changing DateTimeField(auto_now_add=True) to from generating SQL — at Version 2

Reported by: Дилян Палаузов Owned by: nobody
Component: Migrations Version: master
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description (last modified by Дилян Палаузов)

A switch from DateTimeField(auto_now_add=True) to DateTimeField( creates the statements

ALTER TABLE SET DEFAULT '2017-10-16T09:35:52.710695'::timestamp;

which have no effects, apart from locking the whole table twice.

A proposal to recognize, when the effective default-callable doesn't change and skip changing the DEFAULT twice in this case, as well as not generating a migration when this is the only change on a field:

diff --git a/django/db/backends/base/ b/django/db/backends/base/
--- a/django/db/backends/base/
+++ b/django/db/backends/base/
@@ -199,28 +199,33 @@ class BaseDatabaseSchemaEditor(object):
             'requires_literal_defaults must provide a prepare_default() method'
-    def effective_default(self, field):
+    @staticmethod
+    def effective_default_before_callable(field):
-        Returns a field's effective database default value
+        Returns a field's effective database default callable or value
         if field.has_default():
-            default = field.get_default()
+            return field._get_default
         elif not field.null and field.blank and field.empty_strings_allowed:
             if field.get_internal_type() == "BinaryField":
-                default = six.binary_type()
+                return six.binary_type()
-                default = six.text_type()
+                return six.text_type()
         elif getattr(field, 'auto_now', False) or getattr(field, 'auto_now_add', False):
             default =
             internal_type = field.get_internal_type()
             if internal_type == 'DateField':
-                default =
+                return
             elif internal_type == 'TimeField':
-                default = default.time
+                return default.time
             elif internal_type == 'DateTimeField':
-                default =
-        else:
-            default = None
+                return
+    def effective_default(self, field):
+        """
+        Returns a field's effective database default value
+        """
+        default = BaseDatabaseSchemaEditor.effective_default_before_callable(field)
         # If it's a callable, call it
         if callable(default):
             default = default()
@@ -615,6 +620,7 @@ class BaseDatabaseSchemaEditor(object):
             old_default != new_default and
             new_default is not None and
             not self.skip_default(new_field)
+            and BaseDatabaseSchemaEditor.effective_default_before_callable(old_field) != BaseDatabaseSchemaEdit
         if needs_database_default:
             if self.connection.features.requires_literal_defaults:
diff --git a/django/db/models/fields/ b/django/db/models/fields/
--- a/django/db/models/fields/
+++ b/django/db/models/fields/
@@ -1232,7 +1232,7 @@ class DateField(DateTimeCheckMixin, Field):
         if self.auto_now:
             kwargs['auto_now'] = True
         if self.auto_now_add:
-            kwargs['auto_now_add'] = True
+            kwargs['default'] =
         if self.auto_now or self.auto_now_add:
             del kwargs['editable']
             del kwargs['blank']

Change History (2)

comment:1 Changed 19 months ago by Tim Graham

Summary: Migration DateTimeField(auto_now_add=True -> a migration changing DateTimeField(auto_now_add=True) to from generating SQL
Triage Stage: UnreviewedAccepted
Type: UncategorizedCleanup/optimization

comment:2 Changed 19 months ago by Дилян Палаузов

Description: modified (diff)
Note: See TracTickets for help on using tickets.
Back to Top