Django auto_now和auto_now_add

Pau*_*jan 249 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)
Run Code Online (Sandbox Code Playgroud)

更新行时,我得到:

[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)
Run Code Online (Sandbox Code Playgroud)

我的数据库的相关部分是:

  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,
Run Code Online (Sandbox Code Playgroud)

这引起关注吗?

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

jat*_*ism 346

具有auto_now属性集的任何字段也将继承editable=False,因此不会显示在管理面板中.过去曾经有过关于让这些auto_nowauto_now_add论点消失的说法,虽然它们仍然存在,但我觉得你最好只使用自定义save()方法.

因此,为了使这项工作正常,我建议不要使用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)
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!

编辑以回应评论:

我只是坚持重载save()与依赖这些字段参数的原因有两个:

  1. 上述起伏与其可靠性.这些参数严重依赖于Django知道如何与之交互的每种类型的数据库处理日期/时间戳字段,并且似乎在每个版本之间中断和/或更改.(我认为这是完全取消它们的呼吁背后的推动力).
  2. 事实上它们只能在DateField,DateTimeField和TimeField上工作,并且通过使用这种技术,您可以在每次保存项目时自动填充任何字段类型.
  3. 使用django.utils.timezone.now()vs. datetime.datetime.now(),因为它将返回一个TZ感知或天真的datetime.datetime对象取决于settings.USE_TZ.

为了解决OP看到错误的原因,我不确切地知道,但看起来created甚至根本没有填充,尽管有auto_now_add=True.对我而言,它突出了一个错误,并在我上面的小清单中强调了第一项: auto_now并且auto_now_add最多只是片状.

  • 在我的每个模型上编写一个自定义的`save()`比使用`auto_now`要痛苦得多(因为我喜欢在我的所有模型上都有这些字段).那些参数为什么不起作用? (37认同)
  • 但作者问题的根源是什么?auto_now_add有时候工作不正常吗? (9认同)
  • 我强烈反对。1)editable = False是正确的,您不应该编辑该字段,您的数据库需要准确。2)在各种各样的极端情况下,可能不会调用save(),特别是在自定义SQL更新或正在使用的任何情况下。3)这是数据库实际上最擅长的,还有引用完整性等等。信任数据库以使其正确无误是一个很好的默认选择,因为比您(或我)更聪明的头脑设计了数据库以这种方式工作。 (8认同)
  • 我和你在一起德米特里.我很好奇这两个字段为什么会出错...而且我更加好奇为什么你认为编写自己的自定义save()方法更好? (5认同)
  • @TM,但这需要直接使用你的数据库,而Django只针对models.py文件来定义模式 (3认同)
  • 简短的回答是肯定的,此时使用`timezone.now()`会更好.(请记住,当我最初回答这个时,它是在2009年!)我已经更新了答案以反映这一点. (3认同)
  • 对于那些不让Django自动生成ID的用户,您可以处理如下创建的代码:如果不是self.created:self.created = datetime.datetime.now() (2认同)

Sha*_*ger 159

Bah ...评论的声誉不够......但我想指出,在接受的答案中表达的观点有点过时了.根据最近的讨论(django错误#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 = models.AutoDateTimeField(default=timezone.now)
Run Code Online (Sandbox Code Playgroud)

  • 这与auto_add在Django中的作用相同,自2010年以来:https://github.com/django/django/blob/1.8.4/django/db/models/fields/__init__.py#L1447-L1453.除非我在pre_save中需要额外的钩子,否则我坚持使用auto_add. (4认同)
  • 为什么将默认值设置为timezone.now,但pre_save信号是使用datetime.datetime.now? (4认同)

Dat*_*eed 30

谈论一个附带问题:如果你想在admin中看到这个字段(但是你将无法编辑它),你可以添加readonly_fields到你的管理类.

class SomeAdmin(ModelAdmin):
    readonly_fields = ("created","modified",)
Run Code Online (Sandbox Code Playgroud)

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

  • 重要的是要注意:这应该添加到`XxAdmin`类.我读得太快,并试图将它添加到我的`AdminForm`或`ModelForm`类,并且不知道他们为什么不渲染"只读字段".顺便说一句,是否有可能在表格中拥有真正的"只读字段"? (3认同)

Jos*_*osh 26

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

from django.utils import timezone
date_filed = models.DateField(default=timezone.now)
Run Code Online (Sandbox Code Playgroud)

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

  • @nhinkle,你确定你没有指定`default = timezone.now()`而不是推荐的内容:`default = timezine.now`(没有括号)? (8认同)
  • 默认值或多或少等于auto_now_add(首次保存对象时的设置值),但它根本不像auto_now(每次保存对象时设置值). (2认同)
  • @ShaiBerger,我认为它们在一个重要方面存在微妙的不同。该文档指出了其中的微妙之处:“自动设置字段......;它不仅仅是您可以覆盖的默认值。” -- https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.DateField.auto_now_add (2认同)
  • 不工作。它设置默认时间一次。尽管日期发生变化,但它始终使用相同的时间。您必须每天重新启动 django 服务才能保持以下日期正确 (2认同)

Eri*_*eng 17

如果你改变你的模型类如下:

class MyModel(models.Model):
    time = models.DateTimeField(auto_now_add=True)
    time.editable = True
Run Code Online (Sandbox Code Playgroud)

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

  • 是的,它对我有用.Django 1.6.x (2认同)
  • 工作,但它应该是models.DateTimeField而不是models.DatetimeField (2认同)
  • 在'python manage.py makemigrations`中失败:KeyError:u'editable' (2认同)

Edw*_*ell 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
Run Code Online (Sandbox Code Playgroud)

然后,当你想要一个具有这种时间戳行为的模型时,只需要子类:

MyNewTimeStampyModel(TimeStamped):
    field1 = ...
Run Code Online (Sandbox Code Playgroud)

如果您希望字段显示在admin中,则只需删除该editable=False选项即可

  • 好吧有一种情况是last_modified不会更新:当提供`update_fields` arg并且'last_modified'不在列表中时,我会添加:`如果在kwargs中'update_fields'并且'last_modifed'不在kwargs中['update_fields ']:kwargs ['update_fields'].append('last_modified')` (2认同)

Lak*_*sad 5

这值得关注吗?

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

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

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

就像synack所说的,除此之外,在django邮件列表上已经有辩论将其删除,因为它“设计得不好”并且是“黑客”。

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

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

但是,因为auto_addauto_now_add在那里,我会用他们,而不是试图写一个方法我自己。


Vir*_*oop 5

class Feedback(models.Model):
   feedback = models.CharField(max_length=100)
   created = models.DateTimeField(auto_now_add=True)
   updated = models.DateTimeField(auto_now=True)
Run Code Online (Sandbox Code Playgroud)

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

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