Django datetime проблемы (по умолчанию = datetime.now ())

175

У меня такая модель db:

from datetime import datetime    

class TermPayment(models.Model):
    # cut out some fields, non relevant to the question
    date = models.DateTimeField(default=datetime.now(), blank=True)

И когда добавлен новый экземпляр:

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

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

: mysql 5.1.25

django v1.1.1

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

8 ответов

433
Лучший ответ

похоже, что 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 ссылка на поле модели

  • 177
    Важное замечание : использование auto_now_add делает поле недоступным для редактирования в админке
  • 6
    Отличный ответ, спасибо. Есть ли способ выразить более сложное выражение с datetime.now по умолчанию? например, сейчас + 30 дней (следующее не работает) expiry = models.DateTimeField(default=datetime.now + timedelta(days=30))
Показать ещё 11 комментариев
33

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

Справка:

  • 8
    Это очень важное замечание! Если вы используете datetime.now, вы можете использовать местное datetime, которое не знает часовой пояс. Если позже вы сравните его с полем, которое было auto_now_add с помощью auto_now_add (которое auto_now_add часовой пояс), вы можете получить неправильные вычисления из-за различий в часовых поясах.
  • 1
    Это, безусловно, очень важно, но на самом деле сам по себе не отвечает на вопрос.
Показать ещё 1 комментарий
19

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

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

Следовательно, следующее должно работать:

date = models.DateTimeField(default=datetime.now,blank=True)
  • 0
    Это возможно только в django 1.8+
  • 0
    @DavidNathan, почему это? Я думаю, что оба варианта были примерно с 1.4 или ранее, включая использование вызываемого по default ( django-chinese-docs-14.readthedocs.io/en/latest/ref/models/… )
9

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

default=timezone.now

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

Я просто столкнулся с этим. Огромное спасибо Дэвиду.

  • 0
    Большое вам спасибо, это работает отлично!
9

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'

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

5

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

3

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

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

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

date = models.DateTimeField(auto_now=True)

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню