Django auto_now和auto_now_add

时间:2009-11-15 08:47:57

标签: python django datetime django-models django-admin

对于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,

这是值得关注的吗?

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

13 个答案:

答案 0 :(得分:321)

设置了auto_now属性的任何字段也将继承editable=False,因此不会显示在管理面板中。过去一直有人谈论让auto_nowauto_now_add论点消失,尽管它们仍然存在,但我觉得你最好只使用custom save() method

因此,要使其正常运行,我建议您不要使用auto_nowauto_now_add,而是定义自己的save()方法,以确保created仅更新如果未设置id(例如首次创建项目时),则每次保存项目时都会更新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(),因为它会返回TZ感知或天真的datetime.datetime对象,具体取决于settings.USE_TZ
  4. 为了解决OP看到错误的原因,我不确切地知道,但看起来created甚至根本没有填充,尽管auto_now_add=True。对我而言,它突出显示为一个错误,并在我上面的小清单中强调了第1项:auto_nowauto_now_add充其量只是片状。

答案 1 :(得分:152)

答案 2 :(得分:29)

谈论一个附带问题:如果您想在管理员中看到这些字段(但是,您将无法对其进行编辑),则可以将readonly_fields添加到您的管理类。

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

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

答案 3 :(得分:23)

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

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

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

答案 4 :(得分:17)

如果你改变你的模型类:

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

然后,此字段将显示在我的管理员更改页面

答案 5 :(得分:12)

根据我所读到的内容以及到目前为止我对Django的体验,auto_now_add是错误的。我同意jthanism ---覆盖正常的保存方法,它很干净,你知道什么是hapenning。现在,为了让它变干,创建一个名为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选项

即可

答案 6 :(得分:5)

  

这是值得关注的吗?

不,Django会在保存模型时自动为您添加它,因此,它是预期的。

  

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

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

除此之外,正如synack所说,django邮件列表上有一个争论要删除它,因为它“设计得不好”并且是“黑客”

  

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

要痛苦得多

显然,您不必将其写入每个模型。您可以将其写入一个模型并从中继承其他模型。

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

答案 7 :(得分:4)

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 将设置有人修改其反馈的时间。

答案 8 :(得分:2)

对于您的管理员显示,请参阅this answer

注意:auto_nowauto_now_add默认设置为editable=False,这就是适用的原因。

答案 9 :(得分:2)

您可以对已创建的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 int,则auto_now_add会导致错误。

以下是Django的默认DateTimeField.pre_save代码auto_nowauto_now_add

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
  

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

答案 10 :(得分:2)

我今天在工作中需要类似的东西。默认值为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

答案 11 :(得分:1)

auto_now=True在Django 1.4.1中没有为我工作,但下面的代码救了我。这是时区感知日期时间。

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)

答案 12 :(得分:-1)

如果您使用的是南方,并且您希望默认使用将该字段添加到数据库的日期,则答案如下:

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