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_now
和auto_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()
与依赖这些字段参数的原因有两方面:
- 上述的起伏与他们的可靠性。 这些参数在很大程度上依赖于Django知道如何处理date/时间戳字段的每种types的数据库的方式,并且似乎在每个版本之间中断和/或改变。 (我相信是呼吁背后的动力,让他们完全删除)。
- 事实上,他们只能在DateField,DateTimeField和TimeField上工作,并且通过使用这种技术,您可以在每次保存项目时自动填充任何字段types。
- 使用
django.utils.timezone.now()
与datetime.datetime.now()
,因为它会根据settings.USE_TZ
返回一个TZ-aware或者天真的datetime.datetime
对象。
为了解决OP看到错误的原因,我不太清楚,但看起来像created
甚至没有填充,尽pipe有auto_now_add=True
。 对我来说,它突出了一个错误,并在我上面的小列表中强调项目#1: auto_now
和auto_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_now
和auto_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_add
和auto_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