DjangoのDateTimeFieldの詳しい使い方: 日付と時刻を扱うフィールド

319, 2021-09-10

目次

DjangoのDateTimeFieldの詳しい使い方

Djangoのモデルにはいろいろなフィールドがあります。
CharField, TextField, BooleanFieldなどなど。

その中にDateTimeFieldという日付と時刻を扱うフィールドもあります。
このDateTimeFieldを使うと、Djangoのモデルに日付と時刻を記録することが出来ます。

この記事ではDjangoのDateTimeFieldについて詳しく解説します。
結論から言うとDateTimeFieldは↓のように使います。

from django.db import models


class BlueArticle(models.Model):
    title = models.CharField(max_length=128, help_text='記事のタイトル')
    created = models.DateTimeField(help_text='作成日')  # 記事の作成日を表現する

具体的にDateTimeFieldについて、↓の項目を見ていきたいと思います。

  • DateTimeFieldの構造

  • フィールドをDateTimeFieldで定義する

  • デフォルト値を指定する

  • auto_now_addとauto_now引数

  • save()を改造して更新する

  • dateフィルターで日付のフォーマットを指定する

関連記事
DjangoのChoiceFieldのわかりやすい使い方・書き方
DjangoのBooleanFieldの使い方: 真偽値, True, False

DateTimeFieldの構造

DjangoのDateTimeFieldは↓のような構造になっています。

class DateTimeField(auto_now=False, auto_now_add=False, **options)

DateTimeField | Model field reference | Django documentation | Django

DateTimeFieldはhelp_textdefaultなどのDjangoのフィールドではお馴染みの引数もとります。
それに追加してauto_nowauto_now_addという引数も追加されています。

auto_now引数

auto_now引数はデフォルトでFalseです。
Trueを指定すると、モデルのインスタンスが保存されるたびにこのDateTimeFiledのフィールドが更新されるようになります。
つまり、モデルのsave()などを呼び出すと、そのたびにDateTimeFieldのフィールがその時の日付と時刻で自動で更新されます。

この引数はたとえばmodifiedなどのモデルの更新日を表すフィールドを作りたい場合に有効な引数と言えます。
手動でmodifiedの値を更新しなくてもDjangoが自動で値を更新してくれるからです。

auto_now_add引数

auto_now_add引数はデフォルトでFalseです。
Trueを指定するとモデルがDBに保存されたときに値が自動で保存されます。
つまりモデルがDBにINSERTされときにです。

この引数はたとえばcreatedなどのモデルの作成日を表すフィールドを作りたい時に有効な引数です。
DateTimeFieldのフィールドのauto_now_addTrueにしておけば、そのモデルがDBに保存されたときにフィールドの値が自動で更新されます。

フィールドをDateTimeFieldで定義する

DateTimeFieldは他のフィールドと同様にモデルに定義することで使うことが出来ます。
たとえばブログの記事を表現するモデルBlueArticleがあったとして、そのモデルにDateTimeFieldを使ってフィールドcreatedを定義するには↓のようにします。

from django.db import models


class BlueArticle(models.Model):
    title = models.CharField(max_length=128, help_text='記事のタイトル')
    created = models.DateTimeField(help_text='作成日')  # 記事の作成日を表現する

↑の場合、createdというフィールドがDateTimeFieldで作成されたフィールドです。
↑はシンプルにhelp_text引数だけを指定してあとは未指定のままです。

このモデルは↓のようなコードでインスタンスを作成することが出来ます。

from blog.models import BlueArticle
from django.utils import timezone


BlueArticle.objects.create(
    title='青い記事',
    created=timezone.now(),  # 手動でDateTimeFieldを初期化
)

createdフィールドの初期化にはDjangoの便利ツールであるtimezoneを使っています。
timezone.now()で現在時刻をcreatedに設定しています。

この定義で作成したモデルのインスタンスをテンプレートファイルに渡した場合、↓のように参照することが可能です。

<h1>{{ article.title }}</h1>
<p>作成日: {{ article.created }}</p>

出力される画面は↓のようになります。

blue

デフォルト値を指定する

DateTimeFieldの引数defaultにデフォルト値を指定すると、そのフィールドのデフォルトの値を設定することが出来ます。

from django.db import models
from django.utils import timezone


class GreenArticle(models.Model):
    title = models.CharField(max_length=128, help_text='記事のタイトル')
    created = models.DateTimeField(default=timezone.now(), help_text='作成日')    

↑の場合、createdのデフォルト値の設定には例によってDjangoのtimezoneを使っています。
↑のようにコードを書くと、コードが実行されたときのタイムゾーンの値がcreatedのデフォルト値になります。
つまり、このコードにはあまり信頼性がありません。
プロジェクトをPythonで実行した段階でデフォルト値がころころ変わるからです。

現在時刻ではなくモデルの作成日をcreatedなどのフィールドのデフォルト値にしたい場合は、先述と後述するauto_now_add引数を使うか、後述のようにsave()を改造します。

この定義のモデルは↓のようにインスタンスを作成することができます。
createdにはデフォルト値が設定されているので外部からの値の指定は必要ありません。

from blog.models import GreenArticle


GreenArticle.objects.create(
    title='緑色の記事',
)

またこのモデルをテンプレートファイルで参照する場合は↓のようなコードになります。

<h1>{{ article.title }}</h1>
<p>作成日: {{ article.created }}</p>

表示される画面は↓のようになります。

green

ちなみにこのようなデフォルト値の指定を行うと、Djangoから警告されます。
↓は警告内容です。

WARNINGS:
blog.GreenArticle.created: (fields.W161) Fixed default value provided.
        HINT: It seems you set a fixed date / time / datetime value as default for this field. This may not be what you want. If you want to have the current date as default, use `django.utils.timezone.now`

意訳すると「デフォルト値が固定です。createdのデフォルト値に固定の日付を設定していますが、これは現在の日付をデフォルト値にするものではありません。現在の日付を設定したい場合はdjango.utils.timezone.nowを使ってください。」

timezone使ってるやんけ

と思われた方もいると思いますが、要はtimezone.now()で生成しているオブジェクトが現在の日付を表現してないよ、Pythonがコードを実行したときの日付になっちゃうよ、ということです。
これを警告に従って変更するには↓のようなコードを書きます。

from django.db import models
from django.utils import timezone


class YellowArticle(models.Model):
    title = models.CharField(max_length=128, help_text='記事のタイトル')
    created = models.DateTimeField(default=timezone.now, help_text='作成日')    

default=timezone.now()のカッコが取れてdefault=timezone.nowになってます。
こうすればデフォルト値として現在の日付が使われます。
しかしこの方法もネットでは非推奨になっているという指摘が出てきます。
後述のsave()の改造を参照してください。

auto_now_addとauto_now引数

auto_now_addを有効にしたcreatedフィールドと、auto_nowを有効にしたmodifiedフィールドを定義します。

from django.db import models


class RedArticle(models.Model):
    title = models.CharField(max_length=128, help_text='記事のタイトル')
    created = models.DateTimeField(auto_now_add=True, help_text='作成日')
    modified = models.DateTimeField(auto_now=True, help_text='更新日')

先述の通り、auto_now_addはモデルのインスタンスがDBに作成されるときに値が設定されます。
auto_nowはモデルが保存されるたびに値が更新されます。

この定義のモデルは↓のようにインスタンスを作成することができます。

from blog.models import RedArticle


RedArticle.objects.create(
    title='赤い記事',
)

また↓のようにテンプレートファイルで参照可能です。

<h1>{{ article.title }}</h1>
<p>作成日: {{ article.created }}</p>
<p>更新日: {{ article.modified }}</p>

auto_now_add, auto_nowは使うべきか?

実はこの2つの引数は過去から長年にわたってDjangoコミュニティで議論されてきた引数らしく、ネットにはこれらの引数を使うべきではないという意見もあります。
つまり、廃止されるかもしれないので他の方法を使え、ということです。
私は気にせず使っちゃっているんですが、気になるかたは後述の「save()を改造して更新する」を参照してください。

save()を改造して更新する

createdmodifiedに自動で日付を入れたい場合は、↓のようにモデルのsave()メソッドを改造する方法が考えられます。

from django.db import models
from django.utils import timezone


class PurpleArticle(models.Model):
    title = models.CharField(max_length=128, help_text='記事のタイトル')
    created = models.DateTimeField(help_text='作成日')
    modified = models.DateTimeField(help_text='更新日')

    def save(self, *args, **kwargs):
        if not self.id:
            self.created = timezone.now()  # 新規作成時の時刻を保存
        self.modified = timezone.now()  # 保存されるたびに更新
        return super(PurpleArticle, self).save(*args, **kwargs)

この方法が今のところ特に批判も無いベターな方法のようです。

dateフィルターで日付のフォーマットを指定する

createdmodifiedなどのDateTimeFieldのフィールドはテンプレートファイル内で参照できますが、そのまま出力すると日本ではあまりなじみのない表記になってしまいます。
そのためdateフィルターを使って馴染みのあるフォーマットに修正する必要が出てきます。
dateフィルターは↓のように使います。

<h1>{{ article.title }}</h1>
<p>dateフィルターを使うと↓のように表示できる。</p>
<p>作成日: {{ article.created | date:'Y/m/d H:i' }}</p>
<p>更新日: {{ article.modified | date:'y/m/d H:i:s' }}</p>

↑のテンプレートファイルを描画すると↓のような表示の画面になります。

pink

おわりに

今回はDjangoのDateTimeFieldを見てみました。
DjangoのDateTimeField関連の処理はいろいろと非推奨な方法なども散見できて、どれを使えばいいのかわからない感じになってます。
この記事が何かの参考になれば幸いです。

時刻処理を甘く見るプログラマーは

痛い目を見る