Django auto_now 和 auto_now_add

发布于 2024-08-11 15:50:20 字数 647 浏览 8 评论 0原文

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

这是值得关注的原因吗?

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

For Django 1.1.

I have this in my models.py:

class User(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

When updating a row I get:

[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)

The relevant part of my database is:

  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,

Is this cause for concern?

Side question: in my admin tool, those two fields aren't showing up. Is that expected?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

忘年祭陌 2024-08-18 15:50:20

任何带有 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)

编辑以响应评论:

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

  1. 前面提到的其可靠性的起伏。这些参数严重依赖于 Django 知道如何与之交互的每种类型的数据库处理日期/时间戳字段的方式,并且似乎在每个版本之间都会中断和/或更改。 (我相信这是呼吁将它们完全删除的动力)。
  2. 事实上,它们仅适用于 DateField、DateTimeField 和 TimeField,通过使用此技术,您可以在每次保存项目时自动填充任何字段类型。
  3. 使用 django.utils.timezone.now()datetime.datetime.now(),因为它将返回 TZ-aware 或 naive datetime.datetime 对象取决于 settings.USE_TZ

为了解决OP看到错误的原因,我不太清楚,但看起来created根本没有被填充,尽管有auto_now_add=True。对我来说,它是一个错误,并在上面的小列表中强调了第 1 项:auto_nowauto_now_add 充其量是不稳定的。

Any field with the auto_now attribute set will also inherit editable=False and therefore will not show up in the admin panel. There has been talk in the past about making the auto_now and auto_now_add arguments go away, and although they still exist, I feel you're better off just using a custom save() method.

So, to make this work properly, I would recommend not using auto_now or auto_now_add and instead define your own save() method to make sure that created is only updated if id is not set (such as when the item is first created), and have it update modified every time the item is saved.

I have done the exact same thing with other projects I have written using Django, and so your save() would look like this:

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)

Edit in response to comments:

The reason why I just stick with overloading save() vs. relying on these field arguments is two-fold:

  1. The aforementioned ups and downs with their reliability. These arguments are heavily reliant on the way each type of database that Django knows how to interact with treats a date/time stamp field, and seems to break and/or change between every release. (Which I believe is the impetus behind the call to have them removed altogether).
  2. The fact that they only work on DateField, DateTimeField, and TimeField, and by using this technique you are able to automatically populate any field type every time an item is saved.
  3. Use django.utils.timezone.now() vs. datetime.datetime.now(), because it will return a TZ-aware or naive datetime.datetime object depending on settings.USE_TZ.

To address why the OP saw the error, I don't know exactly, but it looks like created isn't even being populated at all, despite having auto_now_add=True. To me it stands out as a bug, and underscores item #1 in my little list above: auto_now and auto_now_add are flaky at best.

小姐丶请自重 2024-08-18 15:50:20

但我想指出,接受的答案中表达的观点有些过时了。根据最近的讨论(django bug #7634#12785),auto_nowauto_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)

But I wanted to point out that the opinion expressed in the accepted answer is somewhat outdated. According to more recent discussions (django bugs #7634 and #12785), auto_now and auto_now_add are not going anywhere, and even if you go to the original discussion, you'll find strong arguments against the RY (as in DRY) in custom save methods.

A better solution has been offered (custom field types), but didn't gain enough momentum to make it into django. You can write your own in three lines (it's Jacob Kaplan-Moss' suggestion).

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)
桃扇骨 2024-08-18 15:50:20

谈论一个附带问题:如果您想在管理中查看此字段(尽管您无法编辑它),您可以将 readonly_fields 添加到您的管理类中。

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

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

Talking about a side question: if you want to see this fields in admin (though, you won't be able to edit it), you can add readonly_fields to your admin class.

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

Well, this applies only to latest Django versions (I believe, 1.3 and above)

吾性傲以野 2024-08-18 15:50:20

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

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

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

I think the easiest (and maybe most elegant) solution here is to leverage the fact that you can set default to a callable. So, to get around admin's special handling of auto_now, you can just declare the field like so:

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

It's important that you don't use timezone.now() as the default value wouldn't update (i.e., default gets set only when the code is loaded). If you find yourself doing this a lot, you could create a custom field. However, this is pretty DRY already I think.

枕花眠 2024-08-18 15:50:20
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 将设置某人修改反馈的时间。

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

Here, we have created and updated columns with a timestamp when created, and when someone modified feedback.

auto_now_add will set the time when an instance is created whereas auto_now will set the time when someone modified his feedback.

西瓜 2024-08-18 15:50:20

如果您像这样更改模型类:

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

那么该字段将显示在我的管理更改页面中

If you alter your model class like this:

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

Then this field will show up in my admin change page

浮云落日 2024-08-18 15:50:20

根据我所读到的内容以及迄今为止使用 Django 的经验,auto_now_add 是有问题的。我同意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 选项

Based on what I've read and my experience with Django so far, auto_now_add is buggy. I agree with jthanism --- override the normal save method it's clean and you know what's hapenning. Now, to make it dry, create an abstract model called 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

And then, when you want a model that has this time-stampy behavior, just subclass:

MyNewTimeStampyModel(TimeStamped):
    field1 = ...

If you want the fields to show up in admin, then just remove the editable=False option

吃→可爱长大的 2024-08-18 15:50:20

这值得关注吗?

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

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

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

添加到上面,正如 synack 所说,django 邮件列表上一直存在关于删除此内容的争论,因为它“设计得不好”并且是“黑客”

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

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

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

Is this cause for concern?

No, Django automatically adds it for you while saving the models, so, it is expected.

Side question: in my admin tool, those 2 fields aren't showing up. Is that expected?

Since these fields are auto added, they are not shown.

To add to the above, as synack said, there has been a debate on the django mailing list to remove this, because, it is "not designed well" and is "a hack"

Writing a custom save() on each of my models is much more pain than using the auto_now

Obviously you don't have to write it to every model. You can write it to one model and inherit others from it.

But, as auto_add and auto_now_add are there, I would use them rather than trying to write a method myself.

笑咖 2024-08-18 15:50:20

至于您的管理员显示,请参阅此答案

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

As for your Admin display, see this answer.

Note: auto_now and auto_now_add are set to editable=False by default, which is why this applies.

美胚控场 2024-08-18 15:50:20

我今天在工作中需要类似的东西。默认值为 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 更改为 < code>datetime.datetime 或更好的 timezone.datetime。我还没有尝试过使用DateTime,仅使用Date

I needed something similar today at work. Default value to be timezone.now(), but editable both in admin and class views inheriting from FormMixin, so for created in my models.py the following code fulfilled those requirements:

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)()
    )

For DateTimeField, I guess remove the .date() from the function and change datetime.date to datetime.datetime or better timezone.datetime. I haven't tried it with DateTime, only with Date.

遗心遗梦遗幸福 2024-08-18 15:50:20

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)

auto_now=True didn't work for me in Django 1.4.1, but the below code saved me. It's for timezone aware 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)
缱倦旧时光 2024-08-18 15:50:20

您可以使用 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 code>, auto_now_add 将导致错误。

这是Django默认 DateTimeField 的代码.pre_saveauto_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

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

You can use timezone.now() for created and auto_now for modified:

from django.utils import timezone
class User(models.Model):
    created = models.DateTimeField(default=timezone.now())
    modified = models.DateTimeField(auto_now=True)

If you are using a custom primary key instead of the default auto- increment int, auto_now_add will lead to a bug.

Here is the code of Django's default DateTimeField.pre_save withauto_now and auto_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)

I am not sure what the parameter add is. I hope it will some thing like:

add = True if getattr(model_instance, 'id') else False

The new record will not have attr id, so getattr(model_instance, 'id') will return False will lead to not setting any value in the field.

笨笨の傻瓜 2024-08-18 15:50:20

您可以使用此代码技术来创建[创建日期](在创建行时自动保存日期,并且不会在每次更新时更新日期和时间)和[更新日期](每次更新行时都会更改日期和时间)。

from django.db import models
from django.utils.timezone import now

class Artical(models.Model):
    
    creation = models.DateTimeField(null=True, default=None, blank=True)
    updation = models.DateTimeField(null=True,default=None,blank=True)

    def save(self, *args, **kwargs):
       
        if not self.creation:
            self.creation = now()

        self.updation = now()

        super(Artical, self).save(*args, **kwargs)

you can use this code technique for make a [creation date] (that automatically save date at creation of row and never update the date and time on every updation ) and a [updation date] (that change date and time every time when you update the row).

from django.db import models
from django.utils.timezone import now

class Artical(models.Model):
    
    creation = models.DateTimeField(null=True, default=None, blank=True)
    updation = models.DateTimeField(null=True,default=None,blank=True)

    def save(self, *args, **kwargs):
       
        if not self.creation:
            self.creation = now()

        self.updation = now()

        super(Artical, self).save(*args, **kwargs)
萌︼了一个春 2024-08-18 15:50:20

关于 update_fields 已有一些评论,但尚未包含在答案中,因此我在这里进行了重要的修订。默认情况下,当 django 保存实例时,它会保存该实例中的每个字段。当只更新一个字段时,这是一种浪费,更经济的方法是只保存正在更改的字段。这是在 django 中使用 update_fields 参数完成的,如下所示:

qt = QueuedTask.objects.create()
qt.status = PENDING
qt.save(update_fields=['status'])

现在您的保存方法应该考虑到这一点,否则在上面的保存中,last_modified 字段也不会保存(请注意,我不确定创建的情况,但由于您可以创建一个新实例,并使用带有更新字段的保存方法,因此我还在保存中包含条件):

def save(self, *args, **kwargs):
    if self.id is None:
        self.created_on = timezone.now()
        update_fields = kwargs.get('update_fields')
        if update_fields is not None and 'created_on' not in update_fields:
            update_fields += ['created_on']
    else:
        self.last_modified = timezone.now()
        update_fields = kwargs.get('update_fields')
        if update_fields is not None and 'last_modified' not in update_fields:
            update_fields += ['last_modified']
    return super(QueuedTask, self).save(*args, **kwargs)

Some comments have been made about update_fields, but it has not been included in an answer, so I am placing an important revision here. By default when django saves an instance, it saves every field in that instance. This is wasteful when only one field is being updated, and a more economical method is to only save the field being changed. This is done in django by using the update_fields parameter as in:

qt = QueuedTask.objects.create()
qt.status = PENDING
qt.save(update_fields=['status'])

Now your save method should take this into consideration, otherwise in the above save, the last_modified field is not also saved (Note that I am not sure about created, but as you could create a new instance, and use save method with update fields, I also include the condition in the save):

def save(self, *args, **kwargs):
    if self.id is None:
        self.created_on = timezone.now()
        update_fields = kwargs.get('update_fields')
        if update_fields is not None and 'created_on' not in update_fields:
            update_fields += ['created_on']
    else:
        self.last_modified = timezone.now()
        update_fields = kwargs.get('update_fields')
        if update_fields is not None and 'last_modified' not in update_fields:
            update_fields += ['last_modified']
    return super(QueuedTask, self).save(*args, **kwargs)
滴情不沾 2024-08-18 15:50:20

如文档中报道的那样:

使用 update() 还可以防止竞争条件,即在加载对象和调用 save() 之间的短时间内数据库中的某些内容可能会发生变化。
最后,要意识到 update() 在 SQL 级别进行更新,因此不会在模型上调用任何 save() 方法,也不会发出 pre_save 或 post_save 信号(这是调用 Model.save( ))。如果您想更新具有自定义 save() 方法的模型的一堆记录,请循环遍历它们并调用 save(),如下所示:

for e in Entry.objects.filter(pub_date__year=2010):
e.comments_on = False
e.save()

Such as is reported in docs:

Using update() also prevents a race condition wherein something might change in your database in the short period of time between loading the object and calling save().
Finally, realize that update() does an update at the SQL level and, thus, does not call any save() methods on your models, nor does it emit the pre_save or post_save signals (which are a consequence of calling Model.save()). If you want to update a bunch of records for a model that has a custom save() method, loop over them and call save(), like this:

for e in Entry.objects.filter(pub_date__year=2010):
e.comments_on = False
e.save()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文