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,
这是否值得担心呢?
另外一个问题:在我的管理工具里,那两个字段没有显示出来。这正常吗?
15 个回答
说到一个附带的问题:如果你想在管理后台看到这些字段(虽然你不能编辑它们),你可以在你的管理类里添加 readonly_fields
。
class SomeAdmin(ModelAdmin):
readonly_fields = ("created","modified",)
不过,这个只适用于最新的Django版本(我记得是1.3及以上的版本)。
我想指出的是,被接受的答案中表达的观点有点过时了。根据最近的讨论(django的bug #7634 和 #12785),auto_now
和 auto_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)
任何设置了 auto_now
属性的字段,都会自动变成 editable=False
,这意味着它们在管理面板中不会显示。之前有人讨论过要把 auto_now
和 auto_now_add
这两个参数去掉,虽然它们现在还在,但我觉得你最好还是用一个 自定义的 save()
方法。
所以,为了让这个功能正常工作,我建议不要使用 auto_now
或 auto_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()
而不是依赖这些字段参数的原因有两个:
- 前面提到的这些参数的可靠性问题。这些参数非常依赖于 Django 能够与之交互的每种数据库如何处理日期/时间戳字段,似乎在每个版本之间都会出问题或发生变化。(我认为这也是有人呼吁完全去掉它们的原因)。
- 它们只适用于 DateField、DateTimeField 和 TimeField,而使用这种方法,你可以在每次保存项目时自动填充任何字段类型。
- 使用
django.utils.timezone.now()
而不是datetime.datetime.now()
,因为前者会根据settings.USE_TZ
返回一个带时区的或不带时区的datetime.datetime
对象。
至于为什么原作者会看到错误,我不太确定,但看起来 created
根本没有被填充,尽管设置了 auto_now_add=True
。在我看来,这显然是个bug,也强调了我上面提到的第一点:auto_now
和 auto_now_add
的可靠性实在是太差了。