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


283

У мене нижче модель db:

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

django v1.1.1


1
Чи не видається можливим за замовчуванням для такої функції, як це?: default=datetime.now- примітка, не викликаючи , як і в now()НЕ стандарт для DateTimeField, але ... зручний ANYCASE.
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
Чудова відповідь, дякую. Чи є спосіб висловити більш складний вираз із датою date.now як за замовчуванням? наприклад зараз + 30 днів (наступне не працює) expiry = models.DateTimeField(default=datetime.now + timedelta(days=30))
michela

26
@michela datetime.now - це функція, і ви намагаєтесь додати до функції timedelta. Вам потрібно буде визначити свій власний зворотний дзвінок, щоб встановити значення за замовчуванням, наприклад def now_plus_30(): return datetime.now() + timedelta(days = 30), а потім скористатисяmodels.DateTimeField(default=now_plus_30)
Carson Myers

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, ви можете вказати місцевий час, який не відомий часовому поясу. Якщо пізніше порівняти його з полем, яке було auto_now_addвідмічене часом ( використовуючи часовий пояс), ви можете отримати неправильні обчислення через помилкові відмінності часових поясів.
Іфтах

1
Це, безумовно, дуже важливо, але насправді не відповідає на це запитання саме собою.
Czechnology

1
остаточно кращий спосіб
Carmine Tambascia

28

З документації на поле за замовчуванням моделі django:

Значення за замовчуванням для поля. Це може бути значення або об'єкт, який можна викликати. Якщо його можна викликати, він буде викликатися кожен раз, коли створюється новий об'єкт.

Тому слід працювати:

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

1
Це можливо лише в джанго 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

Тоді воно буде працювати так, як ви очікували:
нові об’єкти отримуватимуть поточну дату, коли вони створені, але дату не буде перекрито щоразу, коли ви керуєте.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поверталася і потім була оцінена. У мене була така ж проблема з установкою значень по замовчуванням для мого DateTimeFieldз і написав моє рішення тут .


5

З посилання на мову Python під визначеннями функції :

Значення параметрів за замовчуванням оцінюються, коли виконується визначення функції. Це означає, що вираз оцінюється один раз, коли функція визначена, і що те саме "попередньо обчислене" значення використовується для кожного виклику.

На щастя, у Django є спосіб зробити те, що ви хочете, якщо ви використовуєте auto_nowаргумент для DateTimeField:

date = models.DateTimeField(auto_now=True)

Дивіться документи Django для DateTimeField .


0

У Django 3.0, auto_now_addздається, працюєauto_now

reg_date=models.DateField(auto_now=True,blank=True)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.