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,

这值得关注吗?

小问题:在我的管理工具中,这两个字段没有显示。这是意料之中的吗?

363173 次浏览

任何带有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() vs. datetime.datetime.now(),因为它将根据settings.USE_TZ返回一个TZ-aware或naive datetime.datetime对象。

为了解决OP看到错误的原因,我不知道确切的原因,但看起来created甚至根本没有被填充,尽管有auto_now_add=True。对我来说,这是一个bug,并强调了我上面小列表中的第一条:auto_nowauto_now_add充其量是不稳定的。

这值得关注吗?

不,在保存模型时,Django会自动为你添加它,所以,这是意料之中的。

辅助问题:在我的管理工具中,这两个字段没有显示。这是意料之中的吗?

由于这些字段是自动添加的,所以不会显示它们。

为了补充上面的内容,正如synack所说,django邮件列表中一直有关于删除它的争论,因为它“设计得不好”,是“一个黑客”。

在我的每个模型上编写自定义save()比使用auto_now要痛苦得多

显然,你不需要对每个模型都写。您可以将它写入一个模型,并从它继承其他模型。

但是,由于auto_addauto_now_add存在,我会使用它们,而不是自己尝试编写方法。

但我想指出的是,在接受的答案中表达的观点有些过时。根据最近的讨论(django bug # 7634# 12785), auto_nowauto_now_add不会消失,即使你去最初的讨论,你也会在自定义保存方法中发现强烈的反对RY(如DRY)的参数。

已经提供了一个更好的解决方案(自定义字段类型),但还没有获得足够的动力将其纳入django。你可以用三行写自己的语句(它是雅各布·卡普兰-莫斯的建议)。

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)

说一个题外话:如果你想在管理中看到这些字段(尽管,你不能编辑它),你可以将readonly_fields添加到你的管理类中。

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

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

至于你的Admin显示,请参见这个答案

注意:auto_nowauto_now_add默认设置为editable=False,这就是为什么适用于此。

下面是答案,如果你正在使用南,你想默认日期,你添加字段到数据库:

2 .选择“option 然后:datetime.datetime.now () < / p >

看起来是这样的:

$ ./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 e.g. datetime.date.today()
>>> datetime.datetime.now()
+ Added field created_date on myapp.User

我认为这里最简单(也许也是最优雅)的解决方案是利用可以将default设置为可调用对象这一事实。所以,为了避开管理员对auto_now的特殊处理,你可以像这样声明字段:

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

重要的是不要使用timezone.now(),因为默认值不会更新(即,只有在加载代码时才设置默认值)。如果您发现自己经常这样做,您可以创建一个自定义字段。然而,我认为这已经相当DRY了。

如果你像这样修改你的模型类:

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

然后这个字段将显示在我的管理更改页面

auto_now=True在Django 1.4.1中对我不起作用,但下面的代码拯救了我。它用于时区感知datetime。

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()来创建,用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 intauto_now_add将导致错误。

下面是Django默认的带有__abc0和auto_now_addDateTimeField.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

新记录将没有attr id,因此getattr(model_instance, 'id')将返回False,将导致在字段中没有设置任何值。

根据我所读到的和我目前使用Django的经验,auto_now_add是有bug的。我同意jthanism,重写正常的保存方法,这很干净,你知道发生了什么。现在,为了使它变干,创建一个名为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 = ...

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

我今天工作也需要类似的东西。默认值为timezone.now(),但在管理视图和继承自FormMixin的类视图中都可编辑,因此在我的models.py中创建的以下代码满足了这些要求:

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

class Feedback(models.Model):
feedback = models.CharField(max_length=100)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)

在这里,我们创建并更新了列,这些列在创建时和有人修改反馈时都有时间戳。

auto_now_add将在实例创建时设置时间,而auto_now将在某人修改反馈时设置时间。