2009-11-15 30 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。

相關問題