2009-11-15 54 views
178

对于Django 1.1。Django auto_now和auto_now_add

我有这个在我的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, 

是这引起关注?

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

+2

你是否使用自定义主键代替默认自动增量int?我发现使用自定义主键导致此问题。无论如何,我想你现在已经解决了。但该错误仍然存​​在。只是我的0.02 $ – tapan 2011-08-08 17:43:06

+2

还有一件事要提醒。 'update()'方法不会调用'save()'这意味着它不能自动更新'modified'字段 – 2016-03-23 10:42:58

回答

259

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(),因为它会根据settings.USE_TZ返回TZ意识或天真datetime.datetime对象。

为了解决为什么OP看到的错误,我不知道到底,但它看起来像created甚至没有被所有人口,尽管有auto_now_add=True。对我来说,它突出的错误,并在我的小列表中强调项目#1:auto_nowauto_now_add最好是片状。

+8

但是作者问题的根源是什么? auto_now_add有时会不正常工作吗? – 2009-11-15 10:32:38

+4

我和你在一起德米特里。我很好奇这两个字段为什么会抛出错误。而且我更加好奇你为什么认为编写自己的自定义save()方法更好? – hora 2009-11-15 10:51:17

+23

在我的每个模型上写一个自定义的'save()'比使用'auto_now'更痛苦(因为我喜欢在我所有的模型中都有这些字段)。为什么这些参数不起作用? – 2009-11-15 10:53:41

3

Is this cause for concern?

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

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

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

要添加到上面,作为SYNACK说,出现了Django的邮件列表上的辩论,除去这一点,因为,它是“没有设计好”,并且是“黑客攻击”

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

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

但是,由于auto_addauto_now_add在那里,我会使用它们而不是试图自己写一个方法。

123

Bah ...没有足够的声望来发表评论......但我想指出,在接受的答案中表达的意见已经过时了。根据最近的讨论(django错误#7634#12785),auto_now和auto_now_add不会在任何地方出现,即使您转到original discussion,也会发现在自定义保存方法中针对RY(如DRY中)的强烈争论。

已经提供了一个更好的解决方案(自定义字段类型),但没有获得足够的动力将其转化为django。你可以写三行(这是Jacob Kaplan-Moss的建议)。

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) 
+1

三行自定义字段在这里:[链接](https://groups.google.com/d/msg/django-developers/TNYxwiXLTlI/L7srKCO8eEsJ) – hgcrpd 2013-01-25 06:07:01

+0

我不认为自定义字段是必要的,因为你可以将默认设置为可调用(即timezone.now)。请参阅下面的答案。 – Josh 2013-09-11 22:59:17

+4

这与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。 – jwhitlock 2015-09-28 16:09:01

26

谈到一个侧面的问题:如果你想看到在管理这个领域(虽然,你将无法进行编辑),您可以添加readonly_fields您的管理类。

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

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

+3

重要提示:这应该被添加到'XxAdmin'类中。我读得太快,试图将它添加到我的'AdminForm'或'ModelForm'类中,不知道为什么它们没有呈现“只读字段”。顺便说一句,是否有可能在表格中有真正的“只读字段? – 2013-03-04 10:41:29

2

至于你的管理员显示,看this answer

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

0

这里的,如果你正在使用南方的答案,你想默认为你约会的字段添加到数据库:

选择选项 则: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 
+0

更新这将是:datetime和django.utils.timezone模块可用,所以你可以做,例如timezone.now() – 2015-08-18 12:37:02

16

我觉得这里最简单的(也许是最优雅的)解决方案是利用的事实,你可以SE t default可调用。因此,要解决管理员的auto_now的特殊处理,你可以声明栏,像这样:

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

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

+1

默认是或多或少等同于auto_now_add(当对象第一次保存时的设置值),但它完全不像auto_now(每次保存对象时的设置值) – 2013-11-06 14:48:29

+0

@ShaiBerger,我认为它们在一个重要的该文件指出了微妙之处:“自动设置字段...;它不只是一个可以覆盖的默认值。“ - https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.DateField.auto_now_add – 2014-03-16 00:28:32

+0

@ Thomas-BeeDesk:因此,“或多或少相当于” – 2014-03-17 07:13:58

12

我已经找到了解决办法

如果我有一个模型类,如:

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

然后这个领域将在我的管理变革页面显示

+2

是的,它适用于Django 1.6.x – 2014-05-01 09:55:45

+1

但它只能在编辑记录上工作,当我创建新记录时 - 忽略传递给日期的tile值当我改变这个记录 - 设置了新值 – 2014-05-01 10:03:04

+2

工作,但它应该是models.DateTimeField而不是models.DatetimeField – matyas 2016-12-01 13:46:41

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

可以使用timezone.now()用于创建和auto_now改性:如果您使用的是自定义的主键,而不是默认auto- increment int

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

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 

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.

+4

我注意到如果我们保持默认为timezone.now(),当你进行迁移时,实际的日期和时间(这个时刻)被传递给迁移文件。我想我们应该避免这种情况,因为每次你打电话makemigrations这个字段将有不同的价值。 – 2016-04-21 19:16:46

11

基于我读过我与Django的经验,到目前为止,auto_now_add是马车。我同意jthanism ---重写正常的保存方法它是干净的,你知道发生了什么。现在,使其干燥,创建一个名为时间戳的抽象模型:

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 

然后,当你想有这个时间stampy行为的模型,只是子类:

MyNewTimeStampyModel(TimeStamped): 
    field1 = ... 

如果你想字段显示在管理员,然后只删除editable=False选项

+1

你在这里使用了哪个'timezone.now()'?我假设'django.utils.timezone.now()',但我不积极。另外,为什么使用'timezone.now()'而不是'datetime.datetime.now()'? – CoreDumpError 2014-09-02 18:43:34

+1

好点。我添加了导入语句。使用'timezone.now()'的原因是因为它可以识别时区,而'datetime.datetime.now()'是时区初始。你可以在这里阅读:https://docs.djangoproject.com/en/dev/topics/i18n/timezones/ – 2014-09-03 22:20:21

+0

@EdwardNewell你为什么选择在保存中设置creation_date,而不是'default = timezone.now'在字段构造函数中? – Blackeagle52 2015-06-16 13:44:31

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。

相关问题