1
0
mirror of https://github.com/django/django.git synced 2025-10-24 06:06:09 +00:00

Fixed #25005 -- Made date and time fields with auto_now/auto_now_add use effective default.

Thanks to Andriy Sokolovskiy for initial patch.
This commit is contained in:
Iacopo Spalletti
2016-04-02 15:49:32 +02:00
committed by Tim Graham
parent f5ff5010cd
commit 49c57f8565
8 changed files with 243 additions and 9 deletions

View File

@@ -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

View File

@@ -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,

View File

@@ -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)