Django的auto_now和auto_now_add

358 投票
15 回答
415614 浏览
提问于 2025-04-15 16:01

这是关于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,

这是否值得担心呢?

另外一个问题:在我的管理工具里,那两个字段没有显示出来。这正常吗?

15 个回答

47

说到一个附带的问题:如果你想在管理后台看到这些字段(虽然你不能编辑它们),你可以在你的管理类里添加 readonly_fields

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

不过,这个只适用于最新的Django版本(我记得是1.3及以上的版本)。

233

我想指出的是,被接受的答案中表达的观点有点过时了。根据最近的讨论(django的bug #7634#12785),auto_nowauto_now_add 这两个功能并不会消失。即使你去看一下最初的讨论,你会发现有很多强有力的理由反对在自定义保存方法中使用RY(类似于DRY)。

其实有一个更好的解决方案(自定义字段类型),但这个方案没有得到足够的关注,没能被引入到django中。你可以用三行代码自己写一个(这是Jacob Kaplan-Moss的建议)。

from django.db import models
from django.utils import timezone


class AutoDateTimeField(models.DateTimeField):
    def pre_save(self, model_instance, add):
        return timezone.now()

#usage
created_at = models.DateField(default=timezone.now)
updated_at = AutoDateTimeField(default=timezone.now)
477

任何设置了 auto_now 属性的字段,都会自动变成 editable=False,这意味着它们在管理面板中不会显示。之前有人讨论过要把 auto_nowauto_now_add 这两个参数去掉,虽然它们现在还在,但我觉得你最好还是用一个 自定义的 save() 方法

所以,为了让这个功能正常工作,我建议不要使用 auto_nowauto_now_add,而是定义你自己的 save() 方法,确保只有在 id 没有设置的时候(比如在项目第一次创建时),才更新 created,并且每次保存项目时更新 modified

我在用 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 能够与之交互的每种数据库如何处理日期/时间戳字段,似乎在每个版本之间都会出问题或发生变化。(我认为这也是有人呼吁完全去掉它们的原因)。
  2. 它们只适用于 DateField、DateTimeField 和 TimeField,而使用这种方法,你可以在每次保存项目时自动填充任何字段类型。
  3. 使用 django.utils.timezone.now() 而不是 datetime.datetime.now(),因为前者会根据 settings.USE_TZ 返回一个带时区的或不带时区的 datetime.datetime 对象。

至于为什么原作者会看到错误,我不太确定,但看起来 created 根本没有被填充,尽管设置了 auto_now_add=True。在我看来,这显然是个bug,也强调了我上面提到的第一点:auto_nowauto_now_add 的可靠性实在是太差了。

撰写回答