Django datetime问题(default = datetime.now())


283

我有下面的数据库模型:

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)

我的问题:数据库中的所有记录在date字段中都具有相同的值,这是第一次付款的日期。服务器重新启动后,一个记录具有新的日期,其他记录与第一个相同。看起来好像有些数据被缓存了,但是我找不到位置。

数据库:mysql 5.1.25

Django v1.1.1


1
不可能默认为这样的函数吗?:default=datetime.now-注意,不按那样调用now()不是DateTimeField的标准,而是...方便的情况。
viridis

Answers:


596

似乎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是一个函数,并且您试图向该函数添加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)
Michael Mior

34
请注意,使用auto_now_add是一样的使用默认值,因为该领域将始终等于现在(不可编辑)..我知道,这是已经说过,但它不只是一个“管理”页面的梅特
VAShhh

114

而不是使用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(这是对时区的了解),则由于时区错误而导致计算错误。
Iftah

1
这绝对是非常重要的,但本身并不能真正回答问题。
捷克学

1
绝对更好的方法
Carmine Tambascia

28

从django模型默认字段的文档中

字段的默认值。这可以是值或可调用对象。如果可以调用,则每次创建新对象时都会调用它。

因此,以下应该起作用:

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

1
这仅在Django 1.8+中才有可能
David Schumann

@DavidNathan为什么?我认为这两个选项已经出现自1.4或之前,包括使用可调用为defaultdjango-chinese-docs-14.readthedocs.io/en/latest/ref/models/...
马克

16

大卫的回答正确。括号()使得每次评估模型时都调用可调用的 timezone.now()。如果从timezone.now()(如果使用朴素的datetime对象,则从datezone.now()或datetime.now())中删除()来做到这一点:

default=timezone.now

然后它将按您期望的那样工作:
新对象在创建时会收到当前日期,但是每次您进行manage.py makemigrations / migrate时都不会覆盖该日期。

我刚遇到这个。非常感谢David。


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返回函数并评估THEN。我在为设置默认值时遇到了同样的问题,DateTimeField在此处写下了解决方案。


5

从Python语言参考中的“ 函数定义”下

执行功能定义时,将评估默认参数值。这意味着在定义函数时,表达式将被计算一次,并且每次调用都使用相同的“预先计算”值。

幸运的是,如果您将auto_now参数用作,则Django可以执行您想要的操作DateTimeField

date = models.DateTimeField(auto_now=True)

请参阅Django文档中的DateTimeField


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.