This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 3 years have passed since last update.

[Day 34]タイムゾーンと日時オブジェクトを扱う

Posted at

February 16, 2021
←前回:Day 33 画像アップロード機能を付与する

「Djangoを学びたい」とのことでありましたら[Day 1]Djangoの開発環境から読むことをおすすめします。

はじめに

もうDjnagoの特徴や使い方が大分見えてきましたよね。今回はデータベースに保存されている日時を利用して現在時刻との差分を計算していきます。

日時オブジェクトのnativeとawareについて

settings.pyにてUSE_TZ = Trueにしている場合タイムゾーンがサポートされます。この状態ではDjangoはタイムゾーンを認識する日時オブジェクト(awareな日時オブジェクト)を使用します。本サイトのようにstartprojectコマンドによってプロジェクトを生成した場合は初期設定としてUSE_TZ=Trueの設定になっています。よって現状ではawareな日時オブジェクトを使用してきました。タイムゾーンが有効となっている場合、データベースにはUTCで日時を保存しています。Djangoはテンプレートやフォーム等で表示する際に、設定されたタイムゾーンで変換をしています。

データベース

簡単にデータベースについて触れておきたいと思います。本筆者としてはタイムゾーンは常に有効にして使用した方が良いと考えていますが、タイムゾーンの有効・無効を切り替える場合もあると思います。PostgreSQLはタイムゾーン情報をデータベースに保存しているために、タイムゾーンの有効・無効は自由に切り替えられます。しかしそれ以外のデータベースに関してはタイムゾーンがを無効に切り替えた場合にはUTCからネイティブなdatetimeに変換する必要があります。

現在時刻との差分を計算してNEWラベルをつける

さて、今回は演習として一時間以内に新しく作成されたトピックはトップページにNEWラベルを表示するようにしましょう。トップページを表示しているビューはbase/views.pyのTopicListViewクラスですので、ここに手を加えていきます。

base/views.py

from django.utils import timezone

class TopicListView(ListView):
    template_name = 'base/top.html'
    # model = Topic
    queryset = Topic.objects.order_by('-created')
    context_object_name = 'topic_list'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.new_list = []

    def get_queryset(self):
        topic_list = Topic.objects.order_by('-created')
        self.new_list = self._make_new_list(topic_list)
        return topic_list

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx['new_list'] = self.new_list
        return ctx

    def _make_new_list(self, topic_list):
        def pickup_topic(topic):
            now = timezone.now()
            diff = (now - topic.created).total_seconds() / (60 * 60)
            if diff > 1:
                return False
            else:
                return True
        return list(map(lambda x : x.id ,filter(pickup_topic, topic_list)))

解説は不要かと思いますが、現在時刻をtimezone.now()で取得してデータベースに格納されているtopic.createdと差を求めて秒を時間に変換しています。この時間が1時間以下のトピックのIDのみのリストを作成して返していますね。コンテキストにnew_listを渡すためnew_listをインスタンス変数として渡しています。

ラベルを表示するようにテンプレートも変更しましょう。

templates/base/top.html(一部抜粋)

{% extends 'base/base.html' %}
{% block title %}ITについて切磋琢磨する掲示板 - {{ block.super }}{% endblock %}
{% block content %}
<div class="ui grid stackable">
    <div class="eleven wide column">
        <div class="ui breadcrumb">
            <a class="active section">TOP</a>
        </div>
        <div class="ui segment">
            <div class="content">
                <div class="header"><h3>新着スレッド</h3></div>
                <div class="ui divided items">
                    {% for topic in topic_list %}
                    <div class="item">
                        <div class="content">
                            <div class="header">
                                <a href="{% url 'thread:topic' pk=topic.id %}">
                                    <h4>
                                        {% if topic.id in new_list %}
                                        <div class="ui violet horizontal label">new</div>
                                        {% endif %}
                                        {{topic.title}}
                                    </h4>
                                </a>
                            </div>
                            <div class="meta">
                                <span class="name">{{topic.user_name}}</span>
                                <span class="date">{{topic.created}}</span>
                            </div>
                        </div>
                    </div>
                    {% endfor %}
                </div>
            </div>
        </div>
    </div>
    {% include 'base/sidebar.html' %}
</div>
{% endblock %}

全て掲載しましたが、修正点はトピックタイトルの部分のみです。

では確認してみましょう。新規にトピックを作成するとNEWラベルがつきますね。

image.png

おわりに

お久しぶりです。復活しました。Django学習帳が使えなくなってから、体調が悪くなってしまいました。あと、履歴書書いていました。
今日からまた頑張っていきたいと思います。

それではまたまた

←前回:Day 33 画像アップロード機能を付与する
→次回:Day 35

0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up