Loading django/db/backends/base/schema.py +11 −1 Original line number Diff line number Diff line import hashlib import logging from datetime import datetime from django.db.backends.utils import truncate_name from django.db.transaction import atomic from django.utils import six from django.utils import six, timezone from django.utils.encoding import force_bytes logger = logging.getLogger('django.db.backends.schema') Loading Loading @@ -201,6 +202,15 @@ class BaseDatabaseSchemaEditor(object): default = six.binary_type() else: default = six.text_type() elif getattr(field, 'auto_now', False) or getattr(field, 'auto_now_add', False): default = datetime.now() internal_type = field.get_internal_type() if internal_type == 'DateField': default = default.date elif internal_type == 'TimeField': default = default.time elif internal_type == 'DateTimeField': default = timezone.now else: default = None # If it's a callable, call it Loading django/db/migrations/autodetector.py +9 −3 Original line number Diff line number Diff line Loading @@ -802,9 +802,15 @@ class MigrationAutodetector(object): # You can't just add NOT NULL fields with no default or fields # which don't allow empty strings as default. preserve_default = True if (not field.null and not field.has_default() and not field.many_to_many and not (field.blank and field.empty_strings_allowed)): time_fields = (models.DateField, models.DateTimeField, models.TimeField) if (not field.null and not field.has_default() and not field.many_to_many and not (field.blank and field.empty_strings_allowed) and not (isinstance(field, time_fields) and field.auto_now)): field = field.clone() if isinstance(field, time_fields) and field.auto_now_add: field.default = self.questioner.ask_auto_now_add_addition(field_name, model_name) else: field.default = self.questioner.ask_not_null_addition(field_name, model_name) preserve_default = False self.add_operation( Loading django/db/migrations/questioner.py +50 −3 Original line number Diff line number Diff line Loading @@ -76,6 +76,11 @@ class MigrationQuestioner(object): "Do you really want to merge these migrations?" return self.defaults.get("ask_merge", False) def ask_auto_now_add_addition(self, field_name, model_name): "Adding an auto_now_add field to a model" # None means quit return None class InteractiveMigrationQuestioner(MigrationQuestioner): Loading @@ -101,17 +106,36 @@ class InteractiveMigrationQuestioner(MigrationQuestioner): pass result = input("Please select a valid option: ") def _ask_default(self): def _ask_default(self, default=''): """ Prompt for a default value. The ``default`` argument allows providing a custom default value (as a string) which will be shown to the user and used as the return value if the user doesn't provide any other input. """ print("Please enter the default value now, as valid Python") if default: print( "You can accept the default '{}' by pressing 'Enter' or you " "can provide another value.".format(default) ) print("The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now") print("Type 'exit' to exit this prompt") while True: if default: prompt = "[default: {}] >>> ".format(default) else: prompt = ">>> " if six.PY3: # Six does not correctly abstract over the fact that # py3 input returns a unicode string, while py2 raw_input # returns a bytestring. code = input(">>> ") code = input(prompt) else: code = input(">>> ").decode(sys.stdin.encoding) code = input(prompt).decode(sys.stdin.encoding) if not code and default: code = default if not code: print("Please enter some code, or 'exit' (with no quotes) to exit.") elif code == "exit": Loading Loading @@ -186,6 +210,25 @@ class InteractiveMigrationQuestioner(MigrationQuestioner): False, ) def ask_auto_now_add_addition(self, field_name, model_name): "Adding an auto_now_add field to a model" if not self.dry_run: choice = self._choice_input( "You are trying to add the field '{}' with 'auto_now_add=True' " "to {} without a default; the database needs something to " "populate existing rows.\n".format(field_name, model_name), [ "Provide a one-off default now (will be set on all " "existing rows)", "Quit, and let me add a default in models.py", ] ) if choice == 2: sys.exit(3) else: return self._ask_default(default='timezone.now') return None class NonInteractiveMigrationQuestioner(MigrationQuestioner): Loading @@ -196,3 +239,7 @@ class NonInteractiveMigrationQuestioner(MigrationQuestioner): def ask_not_null_alteration(self, field_name, model_name): # We can't ask the user, so set as not provided. return NOT_PROVIDED def ask_auto_now_add_addition(self, field_name, model_name): # We can't ask the user, so act like the user aborted. sys.exit(3) tests/migrations/test_auto_now_add/0001_initial.py 0 → 100644 +19 −0 Original line number Diff line number Diff line # -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): initial = True operations = [ migrations.CreateModel( name='Entry', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.CharField(max_length=255)), ], ), ] tests/migrations/test_auto_now_add/__init__.py 0 → 100644 +0 −0 Empty file added. Loading
django/db/backends/base/schema.py +11 −1 Original line number Diff line number Diff line import hashlib import logging from datetime import datetime from django.db.backends.utils import truncate_name from django.db.transaction import atomic from django.utils import six from django.utils import six, timezone from django.utils.encoding import force_bytes logger = logging.getLogger('django.db.backends.schema') Loading Loading @@ -201,6 +202,15 @@ class BaseDatabaseSchemaEditor(object): default = six.binary_type() else: default = six.text_type() elif getattr(field, 'auto_now', False) or getattr(field, 'auto_now_add', False): default = datetime.now() internal_type = field.get_internal_type() if internal_type == 'DateField': default = default.date elif internal_type == 'TimeField': default = default.time elif internal_type == 'DateTimeField': default = timezone.now else: default = None # If it's a callable, call it Loading
django/db/migrations/autodetector.py +9 −3 Original line number Diff line number Diff line Loading @@ -802,9 +802,15 @@ class MigrationAutodetector(object): # You can't just add NOT NULL fields with no default or fields # which don't allow empty strings as default. preserve_default = True if (not field.null and not field.has_default() and not field.many_to_many and not (field.blank and field.empty_strings_allowed)): time_fields = (models.DateField, models.DateTimeField, models.TimeField) if (not field.null and not field.has_default() and not field.many_to_many and not (field.blank and field.empty_strings_allowed) and not (isinstance(field, time_fields) and field.auto_now)): field = field.clone() if isinstance(field, time_fields) and field.auto_now_add: field.default = self.questioner.ask_auto_now_add_addition(field_name, model_name) else: field.default = self.questioner.ask_not_null_addition(field_name, model_name) preserve_default = False self.add_operation( Loading
django/db/migrations/questioner.py +50 −3 Original line number Diff line number Diff line Loading @@ -76,6 +76,11 @@ class MigrationQuestioner(object): "Do you really want to merge these migrations?" return self.defaults.get("ask_merge", False) def ask_auto_now_add_addition(self, field_name, model_name): "Adding an auto_now_add field to a model" # None means quit return None class InteractiveMigrationQuestioner(MigrationQuestioner): Loading @@ -101,17 +106,36 @@ class InteractiveMigrationQuestioner(MigrationQuestioner): pass result = input("Please select a valid option: ") def _ask_default(self): def _ask_default(self, default=''): """ Prompt for a default value. The ``default`` argument allows providing a custom default value (as a string) which will be shown to the user and used as the return value if the user doesn't provide any other input. """ print("Please enter the default value now, as valid Python") if default: print( "You can accept the default '{}' by pressing 'Enter' or you " "can provide another value.".format(default) ) print("The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now") print("Type 'exit' to exit this prompt") while True: if default: prompt = "[default: {}] >>> ".format(default) else: prompt = ">>> " if six.PY3: # Six does not correctly abstract over the fact that # py3 input returns a unicode string, while py2 raw_input # returns a bytestring. code = input(">>> ") code = input(prompt) else: code = input(">>> ").decode(sys.stdin.encoding) code = input(prompt).decode(sys.stdin.encoding) if not code and default: code = default if not code: print("Please enter some code, or 'exit' (with no quotes) to exit.") elif code == "exit": Loading Loading @@ -186,6 +210,25 @@ class InteractiveMigrationQuestioner(MigrationQuestioner): False, ) def ask_auto_now_add_addition(self, field_name, model_name): "Adding an auto_now_add field to a model" if not self.dry_run: choice = self._choice_input( "You are trying to add the field '{}' with 'auto_now_add=True' " "to {} without a default; the database needs something to " "populate existing rows.\n".format(field_name, model_name), [ "Provide a one-off default now (will be set on all " "existing rows)", "Quit, and let me add a default in models.py", ] ) if choice == 2: sys.exit(3) else: return self._ask_default(default='timezone.now') return None class NonInteractiveMigrationQuestioner(MigrationQuestioner): Loading @@ -196,3 +239,7 @@ class NonInteractiveMigrationQuestioner(MigrationQuestioner): def ask_not_null_alteration(self, field_name, model_name): # We can't ask the user, so set as not provided. return NOT_PROVIDED def ask_auto_now_add_addition(self, field_name, model_name): # We can't ask the user, so act like the user aborted. sys.exit(3)
tests/migrations/test_auto_now_add/0001_initial.py 0 → 100644 +19 −0 Original line number Diff line number Diff line # -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): initial = True operations = [ migrations.CreateModel( name='Entry', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.CharField(max_length=255)), ], ), ]