Django auto_nowおよびauto_now_add


272

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つのフィールドが表示されません。それは予想されますか?


3
デフォルトの自動インクリメント整数の代わりにカスタム主キーを使用していましたか?カスタムの主キーを使用すると、この問題が発生することを発見しました。とにかく、あなたは今までにそれを解決したと思います。しかし、バグはまだ存在しています。ちょうど私の0.02ドル
タパン

3
もう1つ注意する必要があります。update()メソッドは呼び出しませんsave()。つまり、modifiedフィールドを自動的に更新できませんでした
ケミカルプログラマー、

回答:


383

auto_now属性が設定されているフィールドも継承されるeditable=Falseため、管理パネルには表示されません。auto_nowand auto_now_add引数を廃止することについては以前から話がありましたが、それらはまだ存在していますが、カスタムsave()メソッドを使用するだけのほうがいいと思います。

したがって、これを適切に機能させるには、を使用しないauto_nowauto_now_add、代わりに独自のsave()メソッドを定義してcreatedidが設定されていない場合にのみ更新されるようにし(アイテムが最初に作成されたときなど)、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. django.utils.timezone.now()vsを使用します。これは、に応じてdatetime.datetime.now()TZ対応または単純なdatetime.datetimeオブジェクトを返すためsettings.USE_TZです。

OPがエラーを表示した理由に対処するために、正確にはわかりませんが、があるcreatedにもかかわらず、何も入力されていないようですauto_now_add=True。私にはそれがバグとして際立っている、と上記の私の小さなリスト内の項目#1を強調: auto_nowauto_now_add最高の状態でフレーク状です。


9
しかし、著者の問題の原因は何ですか?auto_now_addは時々正しく動作しませんか?
ドミトリーRisenberg 2009年

5
私はドミトリーと一緒です。2つのフィールドがエラーをスローした理由に興味があります。そして、独自のカスタムsave()メソッドを記述する方が良いと思う理由にさらに興味があります。
hora

45
save()各モデルでカスタムを作成することは、auto_now(すべてのモデルでこれらのフィールドを使用したいので)を使用するよりもはるかに困難です。これらのパラメーターが機能しないのはなぜですか?
ポールタージャン、

3
@TM、しかしそれはあなたのデータベースを直接いじる必要がありますが、Djangoはスキーマを定義するためにmodels.pyファイルのみを目的としています
akaihola

11
私は激しく反対します。1)editable = Falseは正しいです。フィールドを編集しないでください。データベースは正確である必要があります。2)特にカスタムSQLの更新などが使用されている場合、save()が呼び出されない可能性のあるあらゆる種類のエッジケースがあります。3)これは、参照整合性などとともに、データベースが実際に得意なことです。データベースを信頼して正しく理解することは、あなたや私よりも賢い心がこのように機能するようにデータベースを設計しているので、良いデフォルトです。
Shayne

175

しかし、私は、受け入れられた回答で表現された意見がやや古くなっていることを指摘したかった。最近のディスカッション(djangoバグ#7634および#12785)によると、auto_nowとauto_now_addはどこにも行かず、元のディスカッションに移動しても、カスタム保存でRY(DRYなど)に対する強力な引数が見つかりますメソッド。

より優れたソリューション(カスタムフィールドタイプ)が提供されましたが、それを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)

1
3行のカスタムフィールドは次のとおりです。リンク
hgcrpd 2013年

デフォルトを呼び出し可能(つまり、timezone.now)に設定できることを考えると、カスタムフィールドは本当に必要ではないと思います。以下の私の答えを参照してください。
Josh

6
これはauto_addがDjangoで行うのと同じことであり、2010年以降はgithub.com/django/django/blob/1.8.4/django/db/models/fields/…となっています。pre_saveに追加のフックが必要でない限り、私はauto_addを使い続けています。
jwhitlock 2015

1
Django 1.9では私にとってはうまくいきませんでした。そのため、auto_now *がこれまでなかったように、このソリューションはどこでも機能しません。すべてのユースケースで機能する唯一のソリューションは( 'update_fields'のarg問題があっても)saveをオーバーライドすることです
danius

4
デフォルトをtimezone.nowに設定しているのに、pre_save信号がdatetime.datetime.nowを使用しているのはなぜですか?
Bobort 2017年

32

副次的な質問について話します:このフィールドを管理者に表示したい場合(ただし、編集することはできません)、readonly_fields管理者クラスに追加できます。

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

まあ、これは最新のDjangoバージョンにのみ適用されます(私は1.3以上)


3
注意することが重要:これはXxAdminクラスに追加する必要があります。私はそれをあまりにも早く読み、自分AdminFormModelFormクラスに追加しようとしましたが、なぜ「読み取り専用フィールド」をレンダリングしないのかわかりませんでした。ところで、フォームに「読み取り専用フィールド」がある可能性はありますか?
Tomasz Gandor 2013年

28

ここで最も簡単な(そしておそらく最もエレガントな)ソリューションはdefault、呼び出し可能に設定できるという事実を利用することだと思います。したがって、auto_nowの管理者の特別な処理を回避するには、次のようにフィールドを宣言するだけです。

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

timezone.now()デフォルト値が更新されないため、使用しないことが重要です(つまり、デフォルトは、コードがロードされたときにのみ設定されます)。これを頻繁に行う場合は、カスタムフィールドを作成できます。しかし、これはもうかなりドライだと思います。


2
デフォルトは、auto_now_add(オブジェクトが最初に保存されるときに値を設定)とほぼ同等ですが、auto_now(オブジェクトが保存されるたびに値を設定)とはまったく異なります。
Shai Berger

1
@ShaiBerger、私は重要な意味で微妙に違うと思います。このドキュメントは、「フィールドを自動的に設定する...;オーバーライドできる単なるデフォルト値ではない」と微妙に述べています。- docs.djangoproject.com/en/dev/ref/models/fields/...
トーマス- BeeDesk

@ Thomas-BeeDesk:そうですね。したがって、「ほぼ同等」です。
Shai Berger 14年

1
マイグレーションを使用している場合、このソリューションはうまく機能しません。実行makemigrationsするたびに、デフォルトはを実行した時間として解釈されるmakemigrationsため、デフォルト値が変更されたと見なされます。
nhinkle

8
@nhinkle、default=timezone.now()推奨されているものではなく、指定していませんdefault=timezine.nowか?(括弧なし)?
Josh

18

次のようにモデルクラスを変更すると、

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

次に、このフィールドは私の管理変更ページに表示されます


1
ただし、編集レコードでのみ機能します。新しいレコードを作成するとき-日付に渡されたタイル値は無視されます。このレコードを変更すると、新しい値が設定されます。
アントンダニルチェンコ2014年

2
機能しますが、models.DatetimeFieldの代わりにmodels.DateTimeFieldにする必要があります
matyas

2
失敗しましたpython manage.py makemigrations:KeyError:u'editable '
laoyur

12

私が読んだこととこれまでの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 = ...

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


1
timezone.now()ここではどちらを使用していますか?私は仮定してdjango.utils.timezone.now()いますが、私は前向きではありません。また、なぜではtimezone.now()なくを使用するのdatetime.datetime.now()ですか?
coredumperror 14

1
良い点。importステートメントを追加しました。使用する理由timezone.now()は、タイムゾーンに対応しているのに対し、datetime.datetime.now()タイムゾーンはナイーブだからです。あなたはそれについてここで読むことができます:docs.djangoproject.com/en/dev/topics/i18n/timezones
Edward Newell

@EdwardNewell default=timezone.nowフィールドコンストラクタ内ではなく、保存時にcreation_dateを設定することを選択したのはなぜですか?
Blackeagle52 2015年

うーん..たぶん私はそれを考えていなかったかもしれません、それはより良い音がします。
エドワードニューウェル2015年

2
とき:まあLAST_MODIFIED意志が更新されない場合がありますupdate_fields:argは提供されており、「LAST_MODIFIED」のリストにない、私は追加しますif 'update_fields' in kwargs and 'last_modifed' not in kwargs['update_fields']: kwargs['update_fields'].append('last_modified')
danius

5

これは心配の種ですか?

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

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

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

上記に加えて、synackが言ったように、djangoメーリングリストでこれを削除するための議論がありました。

モデルごとにカスタムのsave()を記述するのは、auto_nowを使用するよりもはるかに困難です。

もちろん、すべてのモデルに書き込む必要はありません。これを1つのモデルに書き込み、他のモデルから継承することができます。

ではなく、auto_addauto_now_addありますが、私は方法を自分で書こうとするのではなく、それらを使用します。


3

今日は職場でも同様のものが必要でした。デフォルト値はですが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


2

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 intauto_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を返すと、フィールドに値が設定されなくなります。


7
デフォルトをtimezone.now()のままにしておくと、マイグレーションを行うときに、(この瞬間の)実際の日時がマイグレーションファイルに渡されることに気付きました。makemigrationsを呼び出すたびに、このフィールドの値は異なるため、これは避ける必要があると思います。
Karan Kumar

2

管理画面については、こちらの回答をご覧ください。

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


1

auto_now=TrueDjango 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
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は誰かがフィードバックを変更したときに時間を設定します。


-1

南を使用していて、フィールドをデータベースに追加した日付をデフォルトにしたい場合の答えは次のとおりです。

次にオプション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

これは次のように更新されます:datetimeモジュールとdjango.utils.timezoneモジュールが利用できるため、たとえばtimezone.now()を実行できます
michel.iamit

これは、モデルに重要なデータが欠落している場合のアンケートです。モデルを正しく設定すれば、このプロンプトを表示する必要はありません。
Shayne、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.