自動化無しに生活無し

WEBとかAIとかLinux関係をひたすら書く備忘録系ブログ

【Django】年月検索と、年別、月別アーカイブを表示させる【最新と最古のデータから年月リストを作成(Trunc不使用)】

thumbnail

Djangoで月別アーカイブと年月計算を実装させる

元となったコードは40分Djangoにモデルへ投稿日を記録するフィールド(dt)を追加している。

forms.py

from django import forms
from django.core.validators import MinValueValidator,MaxValueValidator

from .models import Topic

class TopicForm(forms.ModelForm):

    class Meta:
        model = Topic
        fields = ["comment"]

#モデルを使用しないフォームクラス
class YearMonthForm(forms.Form):
    year    = forms.IntegerField()
    month   = forms.IntegerField(validators=[MinValueValidator(1),MaxValueValidator(12)])

モデルを使用しないフォームクラスで年と月を数値型で受け取る。月は1から12までの数値なので、validatorsで追加の制約を付与する。

views.py

年月別アーカイブのリストを表示させる。

from django.shortcuts import render,redirect

from django.views import View
from .models import Topic
from .forms import TopicForm,YearMonthForm

class BbsView(View):

    def get(self, request, *args, **kwargs):

        context = {}

        form    = YearMonthForm(request.GET)
        if form.is_valid():
            cleaned = form.clean()

            context["topics"]    = Topic.objects.filter(dt__year=cleaned["year"],dt__month=cleaned["month"]).order_by("-dt")
        else:
            context["topics"]    = Topic.objects.order_by("-dt")

        #月別アーカイブの表示
        #最新と最古のデータを手に入れる。
        newest  = Topic.objects.order_by("-dt").first()
        oldest  = Topic.objects.order_by("dt").first()

        year_month_list = []

        if newest and oldest:

            newest_dt   = newest.dt
            now_year    = oldest.dt.year
            now_month   = oldest.dt.month

            #最古から1ヶ月ずつずらして最新になったら終わり
            while True:
                year_month          = {}
                year_month["link"]  = "?year=" + str(now_year) + "&month=" + str(now_month)
                year_month["label"] = str(now_year) + "年" + str(now_month) + "月"

                copied              = year_month.copy()

                year_month_list.append(copied)

                if now_month >= newest_dt.month and now_year >= newest_dt.year:
                    break
                else:
                    if now_month == 12:
                        now_year += 1
                        now_month = 1
                    else:
                        now_month += 1

        context["year_month_list"]  = year_month_list


        return render(request,"bbs/index.html",context)

    def post(self, request, *args, **kwargs):

        form    = TopicForm(request.POST)
        
        if form.is_valid():
            form.save()

        print(form.errors)

        return redirect("bbs:index")

index   = BbsView.as_view()

先ほどのフォームクラスのYearMonthFormで年月をバリデーション。正しく年月指定されていれば年月で検索をする。

年月のリスト作成は最古から最新に向かって1ヶ月ずつずらす。それをリストにアペンドしていき、できあがった年月のリストをテンプレートでループさせる。

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
	<title>簡易掲示板</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
</head>
<body>

    <main class="container">
        <form method="POST">
            {% csrf_token %}
            <textarea class="form-control" name="comment"></textarea>
            <input type="submit" value="送信">
        </form>

        <div class="row">
            <div class="col-8">
                {% for topic in topics %}
                <div class="border">
                    <div>{{ topic.dt }}</div>
                    <div>{{ topic.comment }}</div>
                </div>
                {% endfor %}
            </div>
            <div class="col-4">
                {% for year_month in year_month_list %}
                <div><a href="{{ year_month.link }}">{{ year_month.label }}</a></div>
                {% endfor %}
            </div>
        </div>

    </main>
</body>
</html>

動かすとこうなる。

selectタグで表現することも可能だが、あえてリンクタグを箇条書きにさせた。

年月別表示

Pythonの場合、pandasを使えば楽に実装できるが、他言語でも実現できるようあえてアルゴリズムを残した。

結論

複雑なアルゴリズムはともかく、年月別に集計してリンクを作りたいだけであれば、下記の記事でも実現はできる。

【Django】年、月、日単位でデータをファイリングする時はTruncを使用する【月ごとの売上、個数などの出力に有効】

ソースコード

https://github.com/seiya0723/year_month_list

スポンサーリンク

シェアボタン

Twitter LINEで送る Facebook はてなブログ