Проблемы с Django для даты и времени (по умолчанию = datetime.now ())


284

У меня есть ниже модель дб:

from datetime import datetime    

class TermPayment(models.Model):
    # I have excluded fields that are irrelevant to the question
    date = models.DateTimeField(default=datetime.now(), blank=True)

Я добавляю новый экземпляр, используя ниже:

tp = TermPayment.objects.create(**kwargs)

Моя проблема: все записи в базе данных имеют одинаковое значение в поле даты, которое является датой первого платежа. После перезапуска сервера одна запись имеет новую дату, а другие записи совпадают с первой. Похоже, что некоторые данные кэшируются, но я не могу найти где.

база данных: mysql 5.1.25

Джанго v1.1.1


1
Не возможно по умолчанию использовать такую ​​функцию, как эта ?: default=datetime.now- обратите внимание, без вызова как в now()Не стандарт для DateTimeField, но ... удобно в любом случае.
viridis,

Ответы:


597

похоже, datetime.now()что оценивается, когда модель определена, а не каждый раз, когда вы добавляете запись.

В Django есть функция для достижения того, что вы уже пытаетесь сделать:

date = models.DateTimeField(auto_now_add=True, blank=True)

или

date = models.DateTimeField(default=datetime.now, blank=True)

Разница между вторым примером и тем, что у вас есть, заключается в отсутствии скобок. Передавая datetime.nowбез скобок, вы передаете фактическую функцию, которая будет вызываться при каждом добавлении записи. Если вы передадите его datetime.now(), вы просто оцените функцию и передадите ей возвращаемое значение.

Более подробная информация доступна в справочнике моделей Django.


206
Важное примечание : использование auto_now_add делает поле недоступным для редактирования в админке
Jiaaro

7
Отличный ответ, спасибо. Есть ли способ выразить более сложное выражение с datetime.now по умолчанию? например, сейчас + 30 дней (следующее не работает) expiry = models.DateTimeField(default=datetime.now + timedelta(days=30))
michela

26
@michela datetime.now - это функция, и вы пытаетесь добавить временную дельту в функцию. Вы должны будете определить свой собственный обратный вызов, чтобы установить значение по умолчанию, например def now_plus_30(): return datetime.now() + timedelta(days = 30), и затем использоватьmodels.DateTimeField(default=now_plus_30)
Карсон Майерс

55
Или вы можете, конечно, сделатьdefault=lambda: datetime.now()+timedelta(days=30)
Майкл Миор

34
ЗНАТЬ , что с помощью auto_now_add это НЕ то же самое, используя по умолчанию , потому что поле будет всегда равно сейчас (неизменяемый) .. Я знаю , что это было уже сказано, но это не просто имеет значения страницы «админ»
VAShhh

115

Вместо использования datetime.nowвы должны действительно использоватьfrom django.utils.timezone import now

Ссылка:

так что перейдите на что-то вроде этого:

from django.utils.timezone import now


created_date = models.DateTimeField(default=now, editable=False)

13
Это очень важное замечание! если вы используете datetime.now, вы можете использовать местное datetime, которое не знает часовой пояс. Если позже вы сравните его с полем, которое было помечено с помощью времени auto_now_add(с учетом часового пояса), вы можете получить неверные расчеты из-за различий в часовых поясах.
Ифта

1
Это, безусловно, очень важно, но на самом деле сам по себе не отвечает на вопрос.
Чехословакия,

1
определенно лучший способ
Кармин Тамбаския

28

Из документации по стандартному полю модели django:

Значение по умолчанию для поля. Это может быть значение или вызываемый объект. Если он вызывается, он будет вызываться каждый раз, когда создается новый объект.

Поэтому должно работать следующее:

date = models.DateTimeField(default=datetime.now,blank=True)

1
Это возможно только в django 1.8+
Дэвид Шуман

@DavidNathan почему это? Я думаю, что оба варианта были примерно с 1.4 или раньше, включая использование вызываемого для default( django-chinese-docs-14.readthedocs.io/en/latest/ref/models/… )
Марк

16

У Дэвида был правильный ответ. Скобки () делает это так , что вызываемая timezone.now () вызывается каждый раз , когда модель оценена. Если вы удалите () из timezone.now () (или datetime.now (), если используете наивный объект datetime), чтобы сделать это просто так:

default=timezone.now

Тогда он будет работать так, как вы ожидаете:
новые объекты получат текущую дату при их создании, но дата не будет переопределена каждый раз, когда вы выполняете manage.py makemigrations / migrate.

Я только что столкнулся с этим. Большое спасибо Дэвиду.


10

datetime.now()Оценивается при создании класса, не тогда , когда новая запись добавляется в базу данных.

Для достижения того, что вы хотите, определите это поле как:

date = models.DateTimeField(auto_now_add=True)

Таким образом, dateполе будет установлено на текущую дату для каждой новой записи.


6

Ответ на этот вопрос на самом деле неправильный.

Автоматическое заполнение значения (auto_now / auto_now_add отличается от значения по умолчанию). Значение по умолчанию будет фактически тем, что видит пользователь, если это совершенно новый объект. Что я обычно делаю, это:

date = models.DateTimeField(default=datetime.now, editable=False,)

Убедитесь, что если вы пытаетесь представить это на странице администратора, вы перечислили это как «read_only» и указали имя поля

read_only = 'date'

Опять же, я делаю это, поскольку мое значение по умолчанию обычно не редактируется, а страницы администратора игнорируют нередактируемые, если не указано иное. Однако есть определенная разница между установкой значения по умолчанию и реализацией auto_add, который здесь является ключевым. Проверьте это!


6

datetime.now()оценивается один раз, когда создается экземпляр вашего класса. Попробуйте удалить круглые скобки, чтобы функция datetime.nowбыла возвращена и ТОГДА оценена. У меня была та же проблема с установкой значений по умолчанию для моих DateTimeFields и я написал свое решение здесь .


5

Из справочника по языку Python, под определениями функций :

Значения параметров по умолчанию оцениваются при выполнении определения функции. Это означает, что выражение вычисляется один раз, когда функция определена, и что одно и то же «предварительно вычисленное» значение используется для каждого вызова.

К счастью, у Django есть способ сделать то, что вы хотите, если вы используете auto_nowаргумент для DateTimeField:

date = models.DateTimeField(auto_now=True)

Смотрите Django документы для DateTimeField .


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.