Django的auto_now和auto_now_add

对于Django 1.1。

我在我的models.py中有这个:

class User(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) 

更新一行时,我得到:

 [Sun Nov 15 02:18:12 2009] [error] /home/ptarjan/projects/twitter-meme/django/db/backends/mysql/base.py:84: Warning: Column 'created' cannot be null [Sun Nov 15 02:18:12 2009] [error] return self.cursor.execute(query, args) 

我的数据库的相关部分是:

  `created` datetime NOT NULL, `modified` datetime NOT NULL, 

这是值得担忧的吗?

侧面的问题:在我的pipe理工具中,这两个字段没有显示出来。 这是预期的吗?

设置了auto_now属性的字段也将inheritanceeditable=False ,因此不会显示在pipe理面板中。 过去一直在谈论如何使auto_nowauto_now_add参数消失,虽然它们仍然存在,但我觉得使用自定义save()方法会更好。

所以,为了使这个工作正常,我build议不要使用auto_now或者auto_now_add ,而是定义你自己的save()方法来确保只有当id没有被设置的时候(比如当这个项目被第一次创build的时候)被创build的时候,每次保存该项目时都更新它。

我已经用我用Django编写的其他项目做了完全相同的事情,所以你的save()将如下所示:

 from django.utils import timezone class User(models.Model): created = models.DateTimeField(editable=False) modified = models.DateTimeField() def save(self, *args, **kwargs): ''' On save, update timestamps ''' if not self.id: self.created = timezone.now() self.modified = timezone.now() return super(User, self).save(*args, **kwargs) 

希望这可以帮助!

编辑回应评论:

我坚持重载save()与依赖这些字段参数的原因有两方面:

  1. 上述的起伏与他们的可靠性。 这些参数在很大程度上依赖于Django知道如何处理date/时间戳字段的每种types的数据库的方式,并且似乎在每个版本之间中断和/或改变。 (我相信是呼吁背后的动力,让他们完全删除)。
  2. 事实上,他们只能在DateField,DateTimeField和TimeField上工作,并且通过使用这种技术,您可以在每次保存项目时自动填充任何字段types。
  3. 使用django.utils.timezone.now()datetime.datetime.now() ,因为它会根据settings.USE_TZ返回一个TZ-aware或者天真的datetime.datetime对象。

为了解决OP看到错误的原因,我不太清楚,但看起来像created甚至没有填充,尽pipe有auto_now_add=True 。 对我来说,它突出了一个错误,并在我上面的小列表中强调项目#1: auto_nowauto_now_add是最好的片状。

巴…没有足够的声望发表评论…但我想指出,在接受的答案expression的意见是有点过时了。 根据最近的讨论(django bugs #7634和# 12785),auto_now和auto_now_add不会去任何地方,即使你去了原来的讨论 ,你会发现在自定义保存RY(如DRY)方法。

一个更好的解决scheme已经提供(自定义字段types),但没有获得足够的动力,使其成为Django。 你可以写三行(这是Jacob Kaplan-Moss的build议)。

 class AutoDateTimeField(models.DateTimeField): def pre_save(self, model_instance, add): return datetime.datetime.now() #usage created_at = models.DateField(default=timezone.now) updated_at = models.AutoDateTimeField(default=timezone.now) 

说一个侧面的问题:如果你想在pipe理中看到这个字段(虽然,你将无法编辑它),你可以添加readonly_fields到你的pipe理类。

 class SomeAdmin(ModelAdmin): readonly_fields = ("created","modified",) 

那么,这只适用于最新的Django版本(我相信,1.3及以上)

我认为最简单的(也许是最优雅的)解决scheme是利用可以将default为可调用的事实。 所以,为了解决pipe理员对auto_now的特殊处理,你可以像这样声明域:

 from django.utils import timezone date_filed = models.DateField(default=timezone.now) 

不要使用timezone.now()作为默认值不会更新是非常重要的timezone.now()例如,只有当代码被加载时,默认值才会被设置)。 如果你发现自己做了很多,你可以创build一个自定义字段。 不过,我觉得这已经很干了。

我find了一个解决scheme

如果我有一个模型类,如:

 class MyModel(models.Model): time = models.DatetimeField(auto_now_add=True) time.editable = True 

然后这个字段会显示在我的pipe理员更改页面

根据我已经阅读的内容以及到目前为止使用Django的经验,auto_now_add是buggy。 我同意jthanism —重写正常的保存方法它是干净的,你知道发生了什么。 现在,为了使其干燥,创build一个名为TimeStamped的抽象模型:

 from django.utils import timezone class TimeStamped(models.Model): creation_date = models.DateTimeField(editable=False) last_modified = models.DateTimeField(editable=False) def save(self, *args, **kwargs): if not self.creation_date: self.creation_date = timezone.now() self.last_modified = timezone.now() return super(TimeStamped, self).save(*args, **kwargs) class Meta: abstract = True 

然后,当你想要一个具有时间戳行为的模型时,只需要子类:

 MyNewTimeStampyModel(TimeStamped): field1 = ... 

如果您希望字段显示在pipe理员中,那么只需删除editable=False选项

您可以使用timezone.now()创build和auto_now修改:

 from django.utils import timezone class User(models.Model): created = models.DateTimeField(default=timezone.now()) modified = models.DateTimeField(auto_now=True) 

如果您使用的是自定义主键而不是默认的auto- increment int ,则auto_now_add会导致错误。

下面是Django默认的带有auto_nowauto_now_add DateTimeField.pre_save的代码:

 def pre_save(self, model_instance, add): if self.auto_now or (self.auto_now_add and add): value = timezone.now() setattr(model_instance, self.attname, value) return value else: return super(DateTimeField, self).pre_save(model_instance, add) 

我不确定参数add是什么。 我希望会有这样的事情:

 add = True if getattr(model_instance, 'id') else False 

新logging不会有attr id ,所以getattr(model_instance, 'id')将返回False将导致不在该字段中设置任何值。

这是值得担忧的吗?

不,Django会在保存模型时自动添加它,所以,这是预期的。

侧面的问题:在我的pipe理工具中,这两个字段没有显示出来。 这是预期的吗?

由于这些字段是自动添加的,因此不会显示。

为了补充上面的内容,正如synack所说的那样,django邮件列表上有一个争论是为了消除这个问题,因为它是“devise不好”,是“黑客”

在我的每个模型上写一个自定义的save()比使用auto_now更加痛苦

显然你不必把它写到每一个模型。 您可以将其写入一个模型并从中inheritance其他模型。

但是,由于auto_addauto_now_add在那里,我会用它们而不是自己写一个方法。

至于你的pipe理员显示,看到这个答案 。

注意:默认情况下,auto_now和auto_now_add被设置为editable = False,这就是适用的原因。

在Django 1.4.1中, auto_now=True并不适用于我,但下面的代码保存了我。 这是时区知道的date时间。

 from django.utils.timezone import get_current_timezone from datetime import datetime class EntryVote(models.Model): voted_on = models.DateTimeField(auto_now=True) def save(self, *args, **kwargs): self.voted_on = datetime.now().replace(tzinfo=get_current_timezone()) super(EntryVote, self).save(*args, **kwargs) 

我今天在工作上需要类似的东西。 默认值是timezone.now(),但是可以在FormMixininheritance的admin和class视图中编辑,所以在我的models.py中创build的下面的代码满足了这些要求:

 from __future__ import unicode_literals import datetime from django.db import models from django.utils.functional import lazy from django.utils.timezone import localtime, now def get_timezone_aware_now_date(): return localtime(now()).date() class TestDate(models.Model): created = models.DateField(default=lazy( get_timezone_aware_now_date, datetime.date)() ) 

对于DateTimeField,我想从函数中删除.date(),并将datetime.date更改为datetime.datetime或更好的timezone.datetime。 我还没有用DateTime试过,只用Date。

如果您使用的是南方,而您希望默认将该字段添加到数据库的date,则可以这样做:

select选项2,然后: datetime.datetime.now()

看起来像这样:

 $ ./manage.py schemamigration myapp --auto ? The field 'User.created_date' does not have a default specified, yet is NOT NULL. ? Since you are adding this field, you MUST specify a default ? value to use for existing rows. Would you like to: ? 1. Quit now, and add a default to the field in models.py ? 2. Specify a one-off value to use for existing columns now ? Please select a choice: 2 ? Please enter Python code for your one-off default value. ? The datetime module is available, so you can do eg datetime.date.today() >>> datetime.datetime.now() + Added field created_date on myapp.User