diff options
| author | Iacopo Spalletti <i.spalletti@nephila.it> | 2016-04-02 15:49:32 +0200 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2016-05-09 07:48:40 -0400 |
| commit | 49c57f8565d4b655de5161d299a053eddfc00bf2 (patch) | |
| tree | d1c18f6f10492bfbbc7e4c62275f19bf0d572835 /django | |
| parent | f5ff5010cd74500785a707ac70b51d292403cddc (diff) | |
Fixed #25005 -- Made date and time fields with auto_now/auto_now_add use effective default.
Thanks to Andriy Sokolovskiy for initial patch.
Diffstat (limited to 'django')
| -rw-r--r-- | django/db/backends/base/schema.py | 12 | ||||
| -rw-r--r-- | django/db/migrations/autodetector.py | 12 | ||||
| -rw-r--r-- | django/db/migrations/questioner.py | 53 |
3 files changed, 70 insertions, 7 deletions
diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py index f91ab72e3d..2fd0d2738c 100644 --- a/django/db/backends/base/schema.py +++ b/django/db/backends/base/schema.py @@ -1,9 +1,10 @@ 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') @@ -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 diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py index 96aa230b24..85e50746db 100644 --- a/django/db/migrations/autodetector.py +++ b/django/db/migrations/autodetector.py @@ -802,10 +802,16 @@ 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() - field.default = self.questioner.ask_not_null_addition(field_name, model_name) + 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( app_label, diff --git a/django/db/migrations/questioner.py b/django/db/migrations/questioner.py index 0315df0630..36144f1a55 100644 --- a/django/db/migrations/questioner.py +++ b/django/db/migrations/questioner.py @@ -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): @@ -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": @@ -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): @@ -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) |
