4
1

More than 1 year has passed since last update.

【Django】№04:dateField日付範囲検索の方法

Posted at

経緯

日付範囲検索する場合に、From~Toの両方に検索日付を入力した場合は問題なかったが、どちらか片方だけを入力した場合にエラーが起きるという問題が発生。
そもそも、範囲検索のロジック記述方法がよくわかっていなかったのが原因。

(以下ロジックの補足)
 ・date_min・・・検索項目From側
 ・date_max・・・検索項目To側
 ・date_start・・・データベースdatetimeField

誤りロジック(パターンA)

report_app/views.py

class c_SerchView(ListView):
    template_name = 'report_app/serch.html'
    model = ReportModel
    def get_queryset(self):
        date_min = self.request.GET.get('date_min')
        date_max = self.request.GET.get('date_max')

        if date_min:
           object_list = ReportModel.objects.filter(date_start__gte=date_min,date_start__lte=date_max)
        else:
           object_list = ReportModel.objects.filter(date_start__gte=today,date_start__lte=today)

        return object_list
i_serch = c_SerchView.as_view()

この場合の動きは下記のとおり。両方入力した時しか正常に検索できない。
× 2020/08/01~空欄 の場合・・・エラー発生
× 空欄~2020/08/31 の場合・・・エラーは出ないが1件も検出されない
◎ 2020/08/01~2020/08/31の場合・・・正常に検出される
× 2020/08/01~2020/08/01の場合・・・1件も検出されない

誤りロジック(パターンB)

report_app/views.py
class c_SerchView(ListView):
    template_name = 'report_app/serch.html'
    model = ReportModel
    context_object_name = 'object_list' #デフォルトがobject_listなので名前を変えない場合は本来これは記載しなくてもいい
    def get_queryset(self):
        date_min = self.request.GET.get('date_min')
        date_max = self.request.GET.get('date_max')

        if date_min:
            object_list = ReportModel.objects.filter(date_start__gte=date_min).order_by('date_start')
        elif date_max:
            object_list = ReportModel.objects.filter(date_start__lte=date_max).order_by('date_start')
        elif date_min and date_max:
            object_list = ReportModel.objects.filter(date_start__range=[date_min, date_max]).order_by('date_start')
        else:
            object_list = ReportModel.objects.filter(date_start__isnull=True)

        return object_list
i_serch = c_SerchView.as_view()

この場合は、
検索項目の両方(20200824~20200831)に日付を入れて範囲検索した場合に、20200824以降の全てが検出される(つまり、to側の範囲が効いてない)

解決ロジック

report_app/views.py
class c_SerchView(ListView):
    template_name = 'report_app/serch.html'
    model = ReportModel
    context_object_name = 'object_list' #デフォルトがobject_listなので名前を変えない場合は本来これは記載しなくてもいい
    def get_queryset(self):
        date_min = self.request.GET.get('date_min')
        date_max = self.request.GET.get('date_max')

        if date_min and date_max:
            object_list = ReportModel.objects.filter(date_start__range=[date_min, date_max]).order_by('date_start')
        elif date_min:
            object_list = ReportModel.objects.filter(date_start__gte=date_min).order_by('date_start')
        elif date_max:
            object_list = ReportModel.objects.filter(date_start__lte=date_max).order_by('date_start')
        else:
            object_list = ReportModel.objects.filter(date_start__isnull=True)

        return object_list
i_serch = c_SerchView.as_view()

これで、なんとか片側検索、両方検索ができるようになりました。

今回はdateFieldなのでこれでOK。
でもこれだとdatetimeFieldの場合は、両側同一日付の検索ができず1件も検出されないという状態になる。。。その解決策は、次№05に記載。

勉強メモ「範囲検索」

基本方針は django_filterを使って、gte, lteを指定するだけ。

■filterメソッドの使い方
 gte ・・・以上
 gt ・・・より大きい(その数値は含まない)
 lte ・・・以下
 lt ・・・未満(その数値は含まない)
 range ・・・の間

例)

report_app/views.py

#指定日以上
object_list = ReportModel.objects.filter(date_start__gte=date_min).order_by('date_start')

#2つの日付間のものを全て(f同一日付検索も可能)
object_list = ReportModel.objects.filter(date_start__range=[date_min, date_max]).order_by('date_start')

#範囲関係なく全て
object_list = ReportModel.objects.all().order_by('date_start').order_by('date_start')
4
1
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
4
1