web-dev-qa-db-ja.com

Djangoのauto_nowとauto_now_add

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,

これは心配の原因ですか。

副次的な質問:私の管理ツールでは、これら2つのフィールドは表示されません。それは予想されますか?

235
Paul Tarjan

auto_now 属性が設定されているフィールドもeditable=Falseを継承するため、管理パネルには表示されません。 auto_nowauto_now_add 引数をなくすことについては過去に話がありました、そしてそれらはまだ存在していますが、 custom save()メソッド を使うほうが得策だと思います。

そのため、これを正しく機能させるには、auto_nowまたはauto_now_addを使用せず、createdが設定されていない場合にのみidが更新されるように独自のsave()メソッドを定義することをお勧めします。アイテムが保存されるたびに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()のオーバーロードに固執する理由は2つあります。

  1. 信頼性と前述の浮き沈み。これらの議論は、Djangoがやり取りする方法を知っている各タイプのデータベースが日付/タイムスタンプフィールドを扱う方法に大きく依存しており、リリースごとに壊れたり変更されたりするようです。 (私が信じているのは、それらを完全に削除させるという呼びかけの背後にある推進力です)。
  2. DateField、DateTimeField、およびTimeFieldでしか機能しないという事実と、この手法を使用することで、アイテムが保存されるたびに任意のフィールドタイプを自動的に設定することができます。
  3. datetime.datetimeに応じてTZ対応オブジェクトまたは単純なsettings.USE_TZオブジェクトが返されるため、Django.utils.timezone.now() vs. datetime.datetime.now()を使用してください。

なぜOPでエラーが発生したのかを説明するために、私は正確にはわかりませんが、auto_now_add=Trueがあってもcreatedはまったく入力されていないようです。私にはそれはバグとして際立っており、上の私の小さなリストの項目#1を強調しています:auto_nowauto_now_addはせいぜい薄片です。

323
jathanism

しかし、私は 受け入れられた答え で表された意見がやや時代遅れであることを指摘したいと思いました。最近の議論によると(Djangoのバグ #7634#12785 ) auto_nowとauto_now_addはどこにも行きません。 最初のディスカッション に進んでも、(DRYのように)RYに対する強い議論が見つかるでしょう。 )カスタム保存方法で。

より良い解決策(カスタムフィールドタイプ)が提供されていますが、Djangoにするための十分な推進力はありませんでした。あなたは3行であなた自身のものを書くことができます(それは 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)
153
Shai Berger

副次的な質問について言えば、あなたがadminでこのフィールドを見たければ(しかし、あなたはそれを編集することができないでしょう)、あなたはあなたのadminクラスにreadonly_fieldsを加えることができます。

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

まあ、これは最新のDjangoバージョンにのみ当てはまります(私は、1.3以上を信じています)

29
DataGreed

ここで最も簡単な(そしておそらく最もエレガントな)解決策は、defaultname__をcallableに設定できるという事実を活用することです。そのため、adminの特別なauto_now処理を回避するには、次のようにフィールドを宣言するだけです。

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

デフォルト値が更新されないため、timezone.now()を使用しないことが重要です(つまり、デフォルトはコードがロードされたときにのみ設定されます)。自分でこれをたくさんやっていると感じたら、カスタムフィールドを作成することができます。しかし、これはすてきなDRYです。

23
Josh

モデルクラスをこのように変更したとします。

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

それからこのフィールドは私の管理者変更ページに表示されます

17
Eric Zheng

これまでに読んだこととこれまでにDjangoで経験したことに基づいて、auto_now_addはバグがあります。私はjthanismに同意します---通常のsaveメソッドを上書きします。それでは、乾燥させるために、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 = ...

フィールドをadminに表示したい場合は、editable=Falseオプションを削除してください。

12
Edward Newell

これは心配の原因ですか。

いいえ、Djangoはモデルを保存している間自動的にそれを追加しますので、それは期待されています。

副次的な質問:私の管理ツールでは、これら2つのフィールドは表示されません。それは予想されますか?

これらのフィールドは自動追加されるため、表示されません。

上記に加えて、synackが言ったように、これを削除することがDjangoメーリングリストで議論されています。

私の各モデルにカスタムのsave()を書くのは、auto_nowを使うよりはるかに面倒です。

明らかに、すべてのモデルにそれを書く必要はありません。あなたはそれを一つのモデルに書いてそれから他のものを継承することができます。

しかし、auto_addauto_now_addがあるので、私は自分でメソッドを書くのではなくそれらを使うことにしました。

5
Lakshman Prasad

Createdにtimezone.now()を、modifiedに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 withauto_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を返し、フィールドに値を設定しないことになります。

2
suhailvs

管理画面については、 この答え を参照してください。

注:auto_nowおよびauto_now_addはデフォルトでeditable=Falseに設定されているため、これが適用されます。

2
jlovison

今日は仕事で似たようなものが必要でした。デフォルト値は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.datedatetime.datetimeまたはそれ以上のtimezone.datetimeに変更すると思います。 DateTimeで試していないが、Dateでのみ試してみた。

2
Tiphareth

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