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:
committed by
Tim Graham
parent
f5ff5010cd
commit
49c57f8565
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user