【cygwin/django】DayArchiveViewクラスのオブジェクトに「%Y-%m-%d」形式のパラメータを渡す


1. 背景

  • チュートリアル、他ではどれも「%Y/%m/%d」形式でパラメータを渡す。
  • これを「%Y-%m-%d」形式で渡したかった。
    • 想定していた方法は、DayArchiveViewクラスが対応していなかった。
    • 次善策で対応した。

2. 環境

  • Windows10/cygwin
  • python3.6.10
  • Django3.1
  • チュートリアルの「mysite」は「config」で作成

3. 各ファイルについて

  • 動作したファイルそのままではなく、手作業で差し障りのない内容に変更しています。
  • converters.py は未変更。

3.1. config/converters.py

from datetime import datetime, date
from config.settings import DEBUG

class YmdConverter:
    regex = '20[0-9]{2}-[01][0-9]-[0123][0-9]'

    def to_python(self, value):
        Ymd = datetime.strptime(value,"%Y-%m-%d")
        if DEBUG: print(Ymd)
        return Ymd

    def to_url(self, value):
        Ymd = date.strftime(value,"%Y-%m-%d")
        if DEBUG: print(Ymd)
        return Ymd
  • DEBUG
    • 設定ファイル中の DEBUG を利用。
  • regex
    • URLパラメータとしての文字列パターンを正規表現で記述。
  • to_python
    • 受け取った文字列をview.pyで利用する時のデータ型に変換する。
  • to_url
    • python内部で使っていたデータをurlパラーメタ-用に変換する。

3.2. myapp/urls.py

該当行のみ引用

from django.urls import register_converter, path
from config import converters
from . import views

register_converter(converters.YmdConverter, 'Ymd')

app_name = "myapp"

urlpatterns = [
    path('<str:shop>/today/'    , views.TodayDiary.as_view() , name='today'),
    path('<str:shop>/<Ymd:day>/', views.DayDiary.as_view(),    name='day'),
]
  • 型名は、%Y-%m-%d に因んで Ymd にした。

3.3. myapp/view.py

from datetime import date
from django.views import generic

from config.settings import DEBUG
from .models import Diary

class TodayDiary(generic.TodayArchiveView):
    model = Diary
    date_field = "entry_date"
    allow_future = True;

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        if DEBUG: print(context)
        return context

class DayDiary(generic.DayArchiveView):
    model = Diary
    allow_future = True
    month_format = "%m"
    date_field = "entry_date"
    context_object_name = "object_list"

    def get_year(self):  return  date.strftime(self.kwargs.get("day",None),"%Y")
    def get_month(self): return  date.strftime(self.kwargs.get("day",None),"%m")
    def get_day(self):   return  date.strftime(self.kwargs.get("day",None),"%d")

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        if DEBUG: print(context)
        return context
  • get_context_data
    • ここにprint文を仕込んでおくと、どんなデータが使えるのか分かって便利。
  • get_year, get_month, get_day
    • year, month, dayをまとめて受け取るメソッドがないっぽい。
    • これで対応するしかなかった。
  • allow_future
    • Trueにすると「未来の日付」が有効になる。普通は未設定(False)にしておくとお思う。
  • month_format
    • デフォルトが %b なので、変えておかないとエラーになる。
  • context_object_name
    • 何も設定しないと、同じ内容で object_list, diary_list の2つが用意される。
      • if DEBUG: print(context) が見やすくなるように追加した。

4. django/views/generic/dates.py

4.1. 現状

521 class BaseDayArchiveView(YearMixin, MonthMixin, DayMixin, BaseDateListView):
522     """List of objects published on a given day."""
523     def get_dated_items(self):
524         """Return (date_list, items, extra_context) for this request."""
525         year = self.get_year()
526         month = self.get_month()
527         day = self.get_day()
528
529         date = _date_from_string(year, self.get_year_format(),
530                                  month, self.get_month_format(),
531                                  day, self.get_day_format())
532
533         return self._get_dated_items(date)
  • year, month, day を個別に受け取る事した想定していない。
  • 本来というか期待している動作になるには、def get_date(self} が必要。

4.2. 希望(未検証)

class BaseDayArchiveView(YearMixin, MonthMixin, DayMixin, BaseDateListView):
    """List of objects published on a given day."""
    def get_date(self):
        """パラメータの状況に合わせてIF文を書く"""
        date = self.date
        if date is None:
            try:
                date = self.kwargs["date"]
            except KeyError:
                try:
                    date = self.request.GET["date"]
                except KeyError:
                    year = self.get_year()
                    month = self.get_month()
                    day = self.get_day()

                    date = _date_from_string(year, self.get_year_format(),
                                             month, self.get_month_format(),
                                             day, self.get_day_format())
        return date

    def get_dated_items(self):
        """Return (date_list, items, extra_context) for this request."""
        date = self.get_date()

        return self._get_dated_items(date)