Django(ジャンゴ)に含まれるデータベースとアクセスするModelには、条件に合ったオブジェクトを取得するfilterメソッドがある。
今回は、作成済の「Django」を利用したアプリケーションに、filterメソッドを利用した検索機能を追加してみたので、そのサンプルプログラムを共有する。
なお、filterメソッドについては、以下のサイトを参照のこと。
https://yu-nix.com/archives/django-filter/
サンプルプログラムの作成
作成したサンプルプログラムの構成は、以下の通り。
なお、上記の赤枠は、前提条件のプログラムから追加・変更したプログラムである。
demoフォルダ下、templatesフォルダ下の検索画面(search.html)の内容は以下の通り。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>検索画面</title> <!-- BootStrap CSS/JSファイルの読み込み --> {% load static %} <link rel="stylesheet" href="{% static 'demo/css/bootstrap.min.css' %}"> <script src="{% static 'demo/js/bootstrap.bundle.min.js' %}"></script> </head> <body> <!-- 画面サイズを画面幅いっぱい(container-fluid)とし、marginを上下左右に設定 --> <div class="container-fluid m-4"> <!-- タイトル行の文字サイズを設定 --> <p class="fs-4">検索条件を指定し、「検索」ボタンを押下してください。</p> <!-- action属性のURLで、(demoフォルダ内)urls.pyの画面遷移先のname属性の値を指定している --> <form action="{% url 'search' %}" method="post"> <!-- 下記csrf_tokenは、CSRF対策を行うことでform送信時エラーを防ぐために設定 --> {% csrf_token %} <!-- 表の幅を、画面横幅の9/12の長さに設定 --> <div class="col-9"> <!-- テーブルの枠線を非表示に設定 --> <table class="table table-borderless"> <!-- 名前 --> <tr> <!-- 画面幅が広くなるにつれてラベルのサイズを狭める設定 --> <td class="col-md-4 col-lg-2"> <!-- フォームとラベルの関連付けを設定 --> <label for="name" class="form-label"> {{ form.name.label_tag }} </label> </td> <td> <span id="name" class="form-label"> {{ form.name }} </span> </td> </tr> <!-- 生年月日(From, To) --> <tr> <td align="left" valign="top"> {{ form.birth_day_from_year.label_tag }} </td> <td> {{ form.birth_day_from_year }}年 {{ form.birth_day_from_month }}月 {{ form.birth_day_from_day }}日~ {{ form.birth_day_to_year }}年 {{ form.birth_day_to_month }}月 {{ form.birth_day_to_day }}日 <!-- 生年月日の入力チェックエラーを表示 --> <span class="text-danger"> <br/>{{ form.non_field_errors.0 }} </span> </td> </tr> <!-- 性別 --> <tr> <td>{{ form.sex.label_tag }}</td> <td> {% for sex_choice in form.sex %} <span class="me-2"> {{ sex_choice.choice_label }} {{ sex_choice.tag }} </span> {% endfor %} </td> </tr> </table> </div> <!-- marginを上に設定 --> <div class="mt-5"> <!-- ボタンの色を青色に設定 --> <input type="submit" name="next" value="検索" class="btn btn-primary"/> </div> </form> </div> </body> </html> |
また、demoフォルダ下、view.pyの内容は以下の通りで、検索画面表示処理(IndexView)、検索処理(SearchView)、一覧画面表示処理(UserListView)を追加している。なお、検索処理ではModelのfilterメソッドを利用している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | from django.shortcuts import render from .forms import InputModelForm from .forms import SearchForm from .models import UserData from django.db.models import Max from datetime import datetime as dt from django.views.generic import TemplateView from django.views.generic import FormView from django.urls import reverse_lazy from django.views.generic import ListView from django.views.generic.edit import DeleteView from django.views.generic.edit import UpdateView from django.db.models import Q from django.core.cache import cache # クラス定数を定義 SEX_LABEL_IDX = 1 # 性別のラベルを取得するためのインデックス class InputView(TemplateView): """ 入力画面を表示するView """ template_name = 'demo/input.html' def get_context_data(self): """ 入力画面に渡すフォーム値を初期化 """ context = super().get_context_data() context['form'] = InputModelForm() return context def post(self, request): """ 入力画面にpostリクエストで遷移 """ return render(request, self.template_name, self.get_context_data()) class ConfirmView(FormView): """ 入力画面で確認ボタンが押下された時の処理を定義するView """ template_name = 'demo/input.html' # form_invalidメソッド実行時に表示する画面 form_class = InputModelForm # 利用するFormクラス success_url = 'demo/confirm.html' # form_validメソッド実行時の画面遷移先 def get_context_data(self, form): """ 入力チェックエラー時に、入力画面にuser_idの値を渡す """ context = super().get_context_data() context['user_id'] = self.request.POST['user_id'] return context def form_valid(self, form): """ フォームの入力チェックを行い、エラーがない場合 """ # 確認画面で表示するための入力値(性別)の値を生成 lbl_sex = UserData.sex_data[int(form.cleaned_data['sex']) - 1][SEX_LABEL_IDX] # 確認画面に渡す各変数を定義し、確認画面に遷移 context = { 'input_form': form, 'lbl_sex': lbl_sex, 'lbl_checked': '確認済', 'user_id': self.request.POST['user_id'] } return render(self.request, self.success_url, context) def form_invalid(self, form): """ フォームの入力チェックを行い、エラーがある場合 """ # フォーム値はそのままで入力画面に戻る return super().form_invalid(form) class RegistView(FormView): """ 確認画面でボタンが押下された時の処理を定義するView """ template_name = 'demo/input.html' # 戻るボタン押下時に表示する画面 form_class = InputModelForm # 利用するFormクラス success_url = reverse_lazy('complete') # 送信ボタン押下後の画面遷移先 def get_context_data(self, form): """ 戻るボタン押下時に、入力画面にフォーム値を渡す """ context = super().get_context_data() context['input_form'] = form context['user_id'] = self.request.POST['user_id'] return context def form_valid(self, form): # 送信ボタンが押下された場合 if "send" in self.request.POST: # 入力データを生成 user_data = UserData() # user_idの値をリクエストから取得 user_id = self.request.POST['user_id'] # user_idが設定済(更新)の場合 if user_id: user_data.id = int(user_id) # user_idが未設定(追加)の場合 else: # USER_DATAテーブルに登録されているidの最大値を取得 max_id_dict = UserData.objects.all().aggregate(Max('id')) # USER_DATAテーブルに登録されているデータが無い場合、id=1とする user_data.id = (max_id_dict['id__max'] or 0) + 1 # 入力データを保存 input_form = InputModelForm(self.request.POST, instance=user_data) input_form.save() # 完了画面に遷移 return super().form_valid(form) # 戻るボタンが押下された場合 elif "back" in self.request.POST: # 確認画面でのフォーム値を入力画面に渡す return super().form_invalid(form) class CompleteView(TemplateView): """ 完了画面を表示するView """ template_name = 'demo/complete.html' class UserDeleteView(DeleteView): """ 一覧画面で削除リンクが押下された時の処理を定義するView """ template_name = 'demo/delete_confirm.html' # 削除確認画面を表示 model = UserData # UserDataモデル(user_dataテーブルと紐づける) success_url = reverse_lazy('index') # 削除ボタン押下後の画面遷移先 class UserUpdateView(UpdateView): """ データ更新時の入力画面を表示するView """ template_name = 'demo/input.html' model = UserData # UserDataモデル(user_dataテーブルと紐づける) form_class = InputModelForm # 利用するFormクラス class IndexView(TemplateView): """ 検索画面を表示するView """ template_name = 'demo/search.html' def get_context_data(self): """ 検索画面に渡すフォーム値を設定 """ context = super().get_context_data() # キャッシュに検索条件データがあれば設定し、キャッシュをクリア context['form'] = SearchForm(cache.get('search_form')) cache.clear() return context class SearchView(FormView): """ 検索画面で検索ボタンが押下された時の処理を定義するView """ template_name = 'demo/search.html' # form_invalidメソッド実行時に表示する画面 form_class = SearchForm # 利用するFormクラス success_url = 'demo/list.html' # form_validメソッド実行時の画面遷移先 def get_context_data(self, form): context = super().get_context_data() return context def form_valid(self, form): """ フォームの入力チェックを行い、エラーがない場合、検索処理を行う """ search_name = Q() # 検索条件(名前) search_birthday_from = Q() # 検索条件(生年月日From) search_birthday_to = Q() # 検索条件(生年月日To) search_sex = Q() # 検索条件(性別) # 指定された名前を含むデータを検索 if form.cleaned_data['name']: search_name = Q(name__icontains=form.cleaned_data['name']) # 指定された生年月日(From)以降のデータを検索 if form.cleaned_data['birth_day_from_year']: search_birthday_from = Q(birth_year__gt=int(form.cleaned_data['birth_day_from_year'])) \ | Q(birth_year=int(form.cleaned_data['birth_day_from_year']) , birth_month__gt=int(form.cleaned_data['birth_day_from_month'])) \ | Q(birth_year=int(form.cleaned_data['birth_day_from_year']) , birth_month=int(form.cleaned_data['birth_day_from_month']) , birth_day__gte=int(form.cleaned_data['birth_day_from_day'])) # 指定された生年月日(To)以前のデータを検索 if form.cleaned_data['birth_day_to_year']: search_birthday_to = Q(birth_year__lt=int(form.cleaned_data['birth_day_to_year'])) \ | Q(birth_year=int(form.cleaned_data['birth_day_to_year']) , birth_month__lt=int(form.cleaned_data['birth_day_to_month'])) \ | Q(birth_year=int(form.cleaned_data['birth_day_to_year']) , birth_month=int(form.cleaned_data['birth_day_to_month']) , birth_day__lte=int(form.cleaned_data['birth_day_to_day'])) # 指定された性別と同じデータを検索 if form.cleaned_data['sex']: search_sex = Q(sex=form.cleaned_data['sex']) # 検索条件に合うデータを検索し一覧に表示 user_list = UserData.objects.filter(search_name, search_birthday_from , search_birthday_to, search_sex) context = { 'user_list': user_list } # 検索条件・一覧検索結果をキャッシュに格納し、一覧画面に遷移 cache.set('search_form', self.request.POST) cache.set('user_list', user_list) return render(self.request, self.success_url, context) def form_invalid(self, form): """ フォームの入力チェックを行い、エラーがある場合 """ # フォーム値はそのままで検索画面に戻る return super().form_invalid(form) class UserListView(TemplateView): """ 一覧画面を表示するView """ template_name = 'demo/list.html' # 一覧画面のHTML def get_context_data(self): """ 一覧画面に渡す検索結果をキャッシュから取得し設定 """ context = super().get_context_data() context['user_list'] = cache.get('user_list') return context |
さらに、demoフォルダ下、urls.pyの内容は以下の通りで、検索画面表示パス(index)、検索処理のパス(search)、一覧画面表示パス(list)を追加している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from django.urls import path from . import views urlpatterns = [ path('', views.IndexView.as_view(), name='index'), path('list', views.UserListView.as_view(), name='list'), path('search', views.SearchView.as_view(), name='search'), path('input', views.InputView.as_view(), name='input'), path('confirm', views.ConfirmView.as_view(), name='confirm'), path('regist', views.RegistView.as_view(), name='regist'), path('complete', views.CompleteView.as_view(), name='complete'), path('<int:pk>/update', views.UserUpdateView.as_view(), name='update'), path('<int:pk>/delete', views.UserDeleteView.as_view(), name='delete'), ] |
また、demoフォルダ下、forms.pyの内容は以下の通りで、検索画面で利用するFormクラス(SearchForm)を追加している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | from django import forms from .models import UserData from django.core.validators import RegexValidator import datetime def check_date(year, month, day): """ 年月日が存在する日付の場合はTrue、存在しない日付の場合はFalseを返す """ try: new_date_str = "%04d/%02d/%02d" % (year, month, day) datetime.datetime.strptime(new_date_str, "%Y/%m/%d") return True except ValueError: return False class UserDataModelForm(forms.ModelForm): """ user_dataテーブルへアクセスするためのモデルに準じたフォーム """ class Meta: # ベースとなるモデルクラス(UserData)を指定 model = UserData # ベースとなるモデルクラスのうち、表示するフィールドを指定 fields = ('name', 'birth_year', 'birth_month', 'birth_day', 'sex', 'memo') labels = { 'name': '名前', 'birth_day': '生年月日', 'sex': '性別', 'memo': 'メモ', } widgets = { 'name': forms.TextInput(attrs={'size': '13', 'maxlength': '13'}), 'birth_year': forms.Select, 'birth_month': forms.Select, 'birth_day': forms.Select, 'sex': forms.RadioSelect(), 'memo': forms.Textarea(attrs={'rows': 6, 'cols': 40}), } def clean(self): """ フォーム内の項目の入力チェック処理 """ cleaned_data = super().clean() birth_year = cleaned_data.get('birth_year') birth_month = cleaned_data.get('birth_month') birth_day = cleaned_data.get('birth_day') # 生年月日が存在しない日付の場合、エラーを返す if not check_date(birth_year, birth_month, birth_day): raise forms.ValidationError('生年月日を正しく入力してください。') return cleaned_data class InputModelForm(UserDataModelForm): """ user_dataテーブルへアクセスするためのモデルに準じたフォームに、項目(check)を追加 """ """ views.pyからは、このInputModelFormクラスを参照している """ check = forms.BooleanField(label='入力確認') class Meta(UserDataModelForm.Meta): fields = UserDataModelForm.Meta.fields + ('check',) class SearchForm(forms.Form): """ 検索画面のフォーム """ # 名前 name = forms.CharField(label='名前', max_length=40, required=False) # 生年月日(From) blank_choice = [('', ''), ] birth_day_from_year = forms.CharField(label='生年月日' , widget=forms.TextInput(attrs={'size': '4'}) , max_length=4, required=False) birth_day_from_month = forms.ChoiceField(choices=blank_choice + UserData.birth_month_data, required=False) birth_day_from_day = forms.ChoiceField(choices=blank_choice + UserData.birth_day_data, required=False) # 生年月日(To) birth_day_to_year = forms.CharField(widget=forms.TextInput( attrs={'size': '4'}), max_length=4, required=False) birth_day_to_month = forms.ChoiceField(choices=blank_choice + UserData.birth_month_data, required=False) birth_day_to_day = forms.ChoiceField(choices=blank_choice + UserData.birth_day_data, required=False) # 性別 sex = forms.ChoiceField(label='性別' , choices=UserData.sex_data , widget=forms.RadioSelect(), required=False) def clean(self): """ フォーム内の項目の入力チェック処理 """ cleaned_data = super().clean() # 生年月日(From)_文字列 birth_day_from_year = cleaned_data.get('birth_day_from_year') birth_day_from_month = cleaned_data.get('birth_day_from_month') birth_day_from_day = cleaned_data.get('birth_day_from_day') # 生年月日(To)_文字列 birth_day_to_year = cleaned_data.get('birth_day_to_year') birth_day_to_month = cleaned_data.get('birth_day_to_month') birth_day_to_day = cleaned_data.get('birth_day_to_day') # 生年月日(From)_数値 int_birth_day_from_year = int(birth_day_from_year or "0") int_birth_day_from_month = int(birth_day_from_month or "0") int_birth_day_from_day = int(birth_day_from_day or "0") # 生年月日(To)_数値 int_birth_day_to_year = int(birth_day_to_year or "0") int_birth_day_to_month = int(birth_day_to_month or "0") int_birth_day_to_day = int(birth_day_to_day or "0") # 生年月日(From)が入力ありで存在しない日付の場合、エラーを返す if not (not birth_day_from_year and not birth_day_from_month and not birth_day_from_day): if not check_date(int_birth_day_from_year, int_birth_day_from_month , int_birth_day_from_day): raise forms.ValidationError('生年月日(From)を正しく入力してください。') # 生年月日(To)が入力ありで存在しない日付の場合、エラーを返す if not (not birth_day_to_year and not birth_day_to_month and not birth_day_to_day): if not check_date(int_birth_day_to_year, int_birth_day_to_month , int_birth_day_to_day): raise forms.ValidationError('生年月日(To)を正しく入力してください。') # 生年月日(From), 生年月日(To)が入力ありで生年月日(From)>生年月日(To)の場合、エラーを返す birth_day_from_str = "%04d/%02d/%02d" \ % (int_birth_day_from_year, int_birth_day_from_month, int_birth_day_from_day) birth_day_to_str = "%04d/%02d/%02d" \ % (int_birth_day_to_year, int_birth_day_to_month, int_birth_day_to_day) if birth_day_from_year and birth_day_from_month and birth_day_from_day: if birth_day_to_year and birth_day_to_month and birth_day_to_day: if birth_day_from_str > birth_day_to_str: raise forms.ValidationError('生年月日(From)が生年月日(To)より大きくなっています。') return cleaned_data |
さらに、入力画面(input.html)、削除確認画面(delete_confirm.html)の内容は以下の通りで、一覧画面に遷移するための「戻る」ボタンを追加している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>入力画面</title> <!-- BootStrap CSS/JSファイルの読み込み --> {% load static %} <link rel="stylesheet" href="{% static 'demo/css/bootstrap.min.css' %}"> <script src="{% static 'demo/js/bootstrap.bundle.min.js' %}"></script> </head> <body> <!-- 画面サイズを画面幅いっぱい(container-fluid)とし、marginを上下左右に設定 --> <div class="container-fluid m-4"> <!-- タイトル行の文字サイズを設定 --> <p class="fs-4">下記必要事項を記載の上、「確認」ボタンを押下してください。</p> <!-- action属性のURLで、(demoフォルダ内)urls.pyの画面遷移先のname属性の値を指定している --> <form action="{% url 'confirm' %}" method="post"> <!-- 下記csrf_tokenは、CSRF対策を行うことでform送信時エラーを防ぐために設定 --> {% csrf_token %} <!-- 表の幅を、画面横幅の9/12の長さに設定 --> <div class="col-9"> <!-- テーブルの枠線を非表示に設定 --> <table class="table table-borderless"> <!-- 名前 --> <tr> <!-- 画面幅が広くなるにつれてラベルのサイズを狭める設定 --> <td class="col-md-4 col-lg-2"> <!-- フォームとラベルの関連付けを設定 --> <label for="name" class="form-label"> {{ form.name.label_tag }} </label> </td> <td> <span id="name" class="form-label"> {{ form.name }} </span> </td> </tr> <!-- 生年月日 --> <tr> <td>{{ form.birth_day.label_tag }}</td> <td> {{ form.birth_year }}年 {{ form.birth_month }}月 {{ form.birth_day }}日 <!-- 生年月日の入力チェックエラーを表示 --> <span class="text-danger"> {{ form.non_field_errors.0 }} </span> </td> </tr> <!-- 性別 --> <tr> <td>{{ form.sex.label_tag }}</td> <td> {% for sex_choice in form.sex %} <span class="me-2"> {{ sex_choice.choice_label }} {{ sex_choice.tag }} </span> {% endfor %} </td> </tr> <!-- メモ --> <tr> <td> <label for="memo" class="form-label"> {{ form.memo.label_tag }} </label> </td> <td> <span id="memo" class="form-label"> {{ form.memo }} </span> </td> </tr> <!-- 入力確認 --> <tr> <td> <label for="check" class="form-label"> {{ form.check.label_tag }} </label> </td> <td> <span id="check" class="form-label"> {{ form.check }} </span> </td> </tr> </table> </div> <!-- ユーザーID --> <!-- 一覧画面で更新リンクを押下したため、object.idが設定されている場合 --> {% if object.id %} <input type="hidden" name="user_id" value="{{ object.id }}"/> <!-- 入力画面で入力チェックエラーが発生したり、確認画面で戻るボタンを 押下したため、user_idが設定されている場合 --> {% elif user_id %} <input type="hidden" name="user_id" value="{{ user_id }}"/> <!-- 一覧画面でデータ追加ボタンを押下したため、object.idもuser_idも 設定されていない場合 --> {% else %} <input type="hidden" name="user_id" value=""/> {% endif %} <!-- marginを上に設定 --> <div class="mt-5"> <!-- ボタンの色を青色に設定 --> <input type="submit" name="confirm" value="確認" class="btn btn-primary"/> <!-- marginを左に設定 --> <span class="ms-4"> <a href="{% url 'list' %}" class="btn btn-primary">戻る</a> </span> </div> </form> </div> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>削除確認画面</title> <!-- BootStrap CSS/JSファイルの読み込み --> {% load static %} <link rel="stylesheet" href="{% static 'demo/css/bootstrap.min.css' %}"> <script src="{% static 'demo/js/bootstrap.bundle.min.js' %}"></script> </head> <body> <!-- 画面サイズを画面幅いっぱい(container-fluid)とし、marginを上下左右に設定 --> <div class="container-fluid m-4"> <!-- タイトル行の文字サイズを設定 --> <p class="fs-4">下記データを削除する場合は、「削除」ボタンを押下してください。</p> <form method="post"> <!-- 下記csrf_tokenは、CSRF対策を行うことでform送信時エラーを防ぐために設定 --> {% csrf_token %} <!-- 表の幅を、画面横幅の8/12の長さに設定 --> <div class="col-8"> <!-- テーブルの枠線を非表示に設定 --> <table class="table table-borderless"> <!-- 名前 --> <tr> <!-- 画面幅が広い場合にラベルのサイズを狭める設定 --> <td class="col-md-4 col-lg-2"> 名前: </td> <td>{{ object.name }}</td> </tr> <!-- 生年月日 --> <tr> <td>生年月日:</td> <td> {{ object.birth_year }}年 {{ object.birth_month }}月 {{ object.birth_day }}日 </td> </tr> <!-- 性別 --> <tr> <td>性別:</td> <td> {% if object.sex|stringformat:"s" == "1" %} 男 {% elif object.sex|stringformat:"s" == "2" %} 女 {% endif %} </td> </tr> <!-- メモ --> <tr> <td>メモ:</td> <td> <!-- 「| default:""」で、Noneを空文字に置き換える --> <!-- テキストエリアの改行を有効にするため、「| linebreaksbr」を付与 --> {{ object.memo | default:"" | linebreaksbr }} </td> </tr> </table> </div> <!-- marginを上に設定 --> <div class="mt-5"> <!-- 削除ボタンの色を赤色に設定 --> <input type="submit" value="削除" class="btn btn-danger"/> <!-- marginを左に設定 --> <span class="ms-4"> <!-- 戻るボタンの色を青色に設定 --> <a href="{% url 'list' %}" class="btn btn-primary">戻る</a> </span> </div> </form> </div> </body> </html> |
また、一覧画面(list.html)、完了画面(complete.html)の内容は以下の通りで、「検索画面に戻る」ボタンを追加している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>一覧画面</title> <!-- BootStrap CSS/JSファイルの読み込み --> {% load static %} <link rel="stylesheet" href="{% static 'demo/css/bootstrap.min.css' %}"> <script src="{% static 'demo/js/bootstrap.bundle.min.js' %}"></script> </head> <body> <!-- 画面サイズを画面幅いっぱい(container-fluid)とし、marginを上下左右に設定 --> <div class="container-fluid m-4"> <!-- タイトル行の文字サイズを設定 --> <p class="fs-4">ユーザーデータテーブル(user_data)の検索データ</p> <!-- 表の幅を、画面横幅の11/12の長さに設定 --> <div class="col-11"> <!-- テーブルの1行おきに背景色を設定 --> <table class="table table-striped"> <!-- テーブルのタイトル行を水色に設定 --> <tr class="table-info"> <th>ID</th> <th>名前</th> <th>生年月日</th> <th>性別</th> <th></th> <th></th> </tr> <!-- 検索条件に合うデータを一覧画面に表示 --> {% for user in user_list %} <tr> <td>{{ user.id }}</td> <td>{{ user.name }}</td> <td>{{ user.birth_year }}年 {{ user.birth_month }}月 {{ user.birth_day }}日 </td> <td> {% if user.sex|stringformat:"s" == "1" %} 男 {% elif user.sex|stringformat:"s" == "2" %} 女 {% endif %} </td> <td> <a href="{% url 'update' user.id %}">更新</a> </td> <td> <a href="{% url 'delete' user.id %}">削除</a> </td> </tr> {% endfor %} </table> </div> <!-- action属性のURLで、(demoフォルダ内)urls.pyの画面遷移先のname属性の値を指定している --> <form action="{% url 'input' %}" method="post"> <!-- 下記csrf_tokenは、CSRF対策を行うことでform送信時エラーを防ぐために設定 --> {% csrf_token %} <!-- marginを上に設定 --> <div class="mt-5"> <!-- ボタンの色を青色に設定 --> <input type="submit" name="next" value="データ追加" class="btn btn-primary"/> <!-- marginを左に設定 --> <span class="ms-4"> <input type="button" name="back" value="検索画面に戻る" class="btn btn-primary" onclick="location.href={% url 'index' %}"/> </span> </div> </form> </div> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>完了画面</title> <!-- BootStrap CSS/JSファイルの読み込み --> {% load static %} <link rel="stylesheet" href="{% static 'demo/css/bootstrap.min.css' %}"> <script src="{% static 'demo/js/bootstrap.bundle.min.js' %}"></script> </head> <body> <!-- 画面サイズを画面幅いっぱい(container-fluid)とし、marginを上下左右に設定 --> <div class="container-fluid m-4"> <!-- タイトル行の文字サイズを設定 --> <p class="fs-4">お申し込みが完了しました。</p> <!-- marginを上に設定 --> <div class="mt-5"> <!-- ボタンの色を青色に設定 --> <a href="{% url 'index' %}" class="btn btn-primary">検索画面に戻る</a> </div> </div> </body> </html> |
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/python/tree/master/django-search/djangoApp
サンプルプログラムの実行
サンプルプログラムの実行結果は以下の通りで、検索処理を正常に行えることが確認できる。
1) 実行前のuser_dataテーブルの中身は、以下の通り。
1 | select * from user_data |
2) コマンドプロンプトでDjangoプロジェクト名のフォルダに移動し、コマンドプロンプトで「python manage.py runserver」コマンドを実行して、Webサーバーを起動する。
3) Webブラウザを起動し、「http://127.0.0.1:8000/demo/」にアクセスすると、以下のように、検索画面(search.html)が表示されることが確認できる。また、何も条件を入力せず「検索」ボタンを押下すると、以下のように、user_dataテーブルの中身が全て一覧画面(list.html)に表示されることが確認できる。
4) 一覧画面(list.html)で「データ追加」ボタンを押下すると、以下のように、入力画面(input.html)⇒確認画面(confirm.html)⇒完了画面(complete.html)に遷移することが確認できる。
5) 完了画面(complete.html)で「検索画面に戻る」ボタンを押下すると、検索画面(search.html)に戻ることが確認できる。
6) データ追加後のuser_dataテーブルの中身は、以下の通り。
1 | select * from user_data |
7) 検索画面で生年月日の範囲を指定し「検索」ボタンを押下すると、指定された範囲の生年月日をもつデータが検索され、一覧画面(list.html)で「検索画面に戻る」ボタンを押下すると、検索画面に戻ることが確認できる。
8) 検索画面で名前を指定し「検索」ボタンを押下すると、指定された名前を含むデータが検索されることが確認できる。
9) 検索画面で性別を指定し「検索」ボタンを押下すると、指定された性別をもつデータが検索されることが確認できる。
10) 検索画面で不正な生年月日を指定すると、以下のように、エラーメッセージが表示されることが確認できる。
11) 一覧画面で「削除」リンクを押下すると、以下のように、削除処理が行われることが確認できる。
12) データ削除後のuser_dataテーブルの中身は、以下の通り。
1 | select * from user_data |
要点まとめ
- Django(ジャンゴ)に含まれるデータベースとアクセスするModelには、条件に合ったオブジェクトを取得するfilterメソッドがあり、検索機能で利用できる。