对于 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,
这值得关注吗?
附带问题:在我的管理工具中,这两个字段没有显示。这是预期的吗?
update()
方法不会调用 save()
,这意味着它不能自动更新 modified
字段
具有 auto_now
属性集的任何字段也将继承 editable=False
,因此不会显示在管理面板中。过去曾有人谈论过让 auto_now
和 auto_now_add
参数消失,尽管它们仍然存在,但我觉得你最好只使用 custom save()
method。
因此,为了使其正常工作,我建议不要使用 auto_now
或 auto_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()
而不是依赖这些字段参数的原因有两个:
上述起伏与他们的可靠性。这些参数在很大程度上依赖于 Django 知道如何与之交互的每种类型的数据库处理日期/时间戳字段的方式,并且似乎在每个版本之间中断和/或更改。 (我相信这是呼吁将它们完全移除的动力)。它们仅适用于 DateField、DateTimeField 和 TimeField,并且通过使用此技术,您可以在每次保存项目时自动填充任何字段类型。使用 django.utils.timezone.now() 与 datetime.datetime.now(),因为它会根据 settings.USE_TZ 返回一个 TZ 感知或天真的 datetime.datetime 对象。
为了解决 OP 看到错误的原因,我不确切知道,但看起来 created
甚至没有被填充,尽管有 auto_now_add=True
。对我来说,它是一个错误,并强调了上面我的小列表中的第 1 项:auto_now
和 auto_now_add
充其量是片状的。
但我想指出,accepted answer 中表达的观点有些过时。根据最近的讨论(django 错误 #7634 和 #12785),auto_now
和 auto_now_add
不会去任何地方,即使你去 original discussion,你也会发现反对 RY 的有力论据(如在 DRY 中)在自定义保存方法中。
已经提供了一个更好的解决方案(自定义字段类型),但没有获得足够的动力使其进入 django。您可以用三行编写自己的代码(它是 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)
谈到一个附带问题:如果您想在 admin 中查看此字段(但是,您将无法对其进行编辑),您可以将 readonly_fields
添加到您的 admin 类中。
class SomeAdmin(ModelAdmin):
readonly_fields = ("created","modified",)
好吧,这仅适用于最新的 Django 版本(我相信,1.3 及更高版本)
XxAdmin
类中。我读得太快了,试图将它添加到我的 AdminForm
或 ModelForm
类中,但不知道他们为什么不呈现“只读字段”。顺便说一句,是否有可能在表单中有真正的“只读字段”?
我认为这里最简单(也许也是最优雅)的解决方案是利用您可以将 default
设置为可调用的事实。因此,要绕过管理员对 auto_now 的特殊处理,您可以像这样声明该字段:
from django.utils import timezone
date_field = models.DateField(default=timezone.now)
重要的是不要使用 timezone.now()
,因为默认值不会更新(即,只有在加载代码时才会设置默认值)。如果您发现自己经常这样做,您可以创建一个自定义字段。然而,我认为这已经很干了。
makemigrations
时,它都会将默认值解释为运行 makemigrations
的时间,因此认为默认值已更改!
default=timezone.now()
而不是推荐的内容:default=timezine.now
(无括号)?
如果您像这样更改模型类:
class MyModel(models.Model):
time = models.DateTimeField(auto_now_add=True)
time.editable = True
然后此字段将显示在我的管理员更改页面中
python manage.py makemigrations
失败:KeyError: u'editable'
根据我迄今为止阅读的内容和我对 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
选项
timezone.now()
?我假设 django.utils.timezone.now()
,但我并不肯定。另外,为什么使用 timezone.now()
而不是 datetime.datetime.now()
?
timezone.now()
的原因是因为它是时区感知的,而 datetime.datetime.now()
是时区幼稚的。您可以在此处阅读:docs.djangoproject.com/en/dev/topics/i18n/timezones
default=timezone.now
?
update_fields
arg 并且 'last_modified' 不在列表中时,我会添加:if 'update_fields' in kwargs and 'last_modifed' not in kwargs['update_fields']: kwargs['update_fields'].append('last_modified')
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 将设置有人修改他的反馈的时间。
这值得关注吗?
不,Django 在保存模型时会自动为您添加它,因此,这是意料之中的。
附带问题:在我的管理工具中,这两个字段没有显示。这是预期的吗?
由于这些字段是自动添加的,因此不会显示。
补充一下,正如 synack 所说,django 邮件列表上一直有争论要删除它,因为它“设计得不好”并且是“黑客”
在我的每个模型上编写自定义 save() 比使用 auto_now 更痛苦
显然,您不必将其写入每个模型。您可以将其写入一个模型并从中继承其他模型。
但是,由于存在 auto_add
和 auto_now_add
,我会使用它们而不是尝试自己编写方法。
我今天在工作中需要类似的东西。默认值为 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
。
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)
您可以使用 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_now
和 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)
我不确定参数 add
是什么。我希望它会是这样的:
add = True if getattr(model_instance, 'id') else False
新记录将没有 attr id,因此 getattr(model_instance, 'id') 将返回 False 将导致未在该字段中设置任何值。
default=timezone.now
(无括号)在创建/修改时调用该函数,而不是在迁移时。
如果您使用的是 south 并且希望默认为将字段添加到数据库的日期,那么这就是答案:
然后选择选项 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
save()
比使用auto_now
更痛苦(因为我喜欢在我的所有模型上都有这些字段)。为什么这些参数不起作用?