・django2.2/python3.8
・models、formsの基礎知識がある
・getとpostの理解ができている
・djangoのテンプレートの理解ができている
コンテンツ
クエリ後のページングの問題点
クエリでのパラメーターをそのまま引き継いでページングをしてくれれば良いのですが、そんな都合良くdjangoさんは動いてくれません。
今回はクエリでパラメーターを検索したあともきちんとパラメーターを引き継いで2ページ目へと画面遷移できるコードを書いてみます。
チュートリアルのページング
ページングに関してはdjangoチュートリアルに習ってコードを作成すればすんなりと動作はします。
djangoチュートリアルのページネーションに書かれたページへ飛ぶ
上記のチュートリアルを理解した上でコードを下記のように書きました。
formに対してバリデーションをかけていますが、こちらのコードでまずはページング処理がきちんと行われます。
views.py
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 |
from django.views.decorators.http import require_http_methods from django.core.paginator import Paginator, PageNotAnInteger,EmptyPage @require_http_methods(['GET']) def inquiry_list(request): qs = Inquiry.objects.order_by('-updated_at') form = InquiryFindForm(request.GET) if not form.is_valid(): return render(request, 'inquiry_apps/inquiry_list.html', {'form': form}) if form.is_valid(): id_int = form.cleaned_data['id'] email_str = form.cleaned_data['email'] page_int = form.cleaned_data['page'] word_str = form.cleaned_data['word'] if email_str is not None: qs = qs.filter(email__contains=email_str) if id_int is not None: qs = qs.filter(id=id_int) if word_str is not None: qs = qs.filter(Q(subject__contains=word_str) | Q(message__contains=word_str)) paginator = Paginator(qs, 5) try: inquiries_page = paginator.page(page_int) except EmptyPage: raise Http404('Page does not exist') context = { 'form': form, 'search_email': email_str, 'inquiries_page': inquiries_page, } template = loader.get_template('inquiry_apps/inquiry_list.html') return HttpResponse(template.render(context, request)) |
このviewsに対応するformが下記。InquiryFindFormになります。
form.py
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 |
from django import forms class InquiryFindForm(forms.Form): id = forms.IntegerField(required=False, min_value=1, max_value=100000) email = forms.CharField(required=False, max_length=255) page = forms.IntegerField(required=False, max_value=10000, min_value=1,) word = forms.CharField(required=False, max_length=1000) def clean(self): cleaned_data = super().clean() return cleaned_data def clean_id(self): find_id = self.cleaned_data['id'] return find_id def clean_email(self): email = self.cleaned_data['email'] return email def clean_page(self): page =self.cleaned_data['page'] if page == None: page = 1 return page def clean_word(self): word = self.cleaned_data['word'] return word |
modelsのコードも念のため記述しておきます
model.py
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 |
from django.db import models class Inquiry(models.Model): class Meta: db_table = 'inquiry' name = models.CharField(verbose_name='name', max_length=255) subject = models.CharField(verbose_name='subject', max_length=255) message = models.CharField(verbose_name='message', max_length=500) email = models.EmailField(verbose_name='email') created_at = models.DateTimeField(verbose_name='created_at', auto_now_add=True) updated_at = models.DateTimeField(verbose_name='updated_at', auto_now_add=True) class InquiryStatus(): Pending = 0 Ignore = 1 Completed = 2 INQUIRY_STATUS_CHOICES = [ (InquiryStatus.Pending, 'Pending'), (InquiryStatus.Ignore, 'Ignore'), (InquiryStatus.Completed, 'Completed'), ] inquiry_status = models.IntegerField( verbose_name='inquiry_status', choices=INQUIRY_STATUS_CHOICES, default=0 ) def __str__(self): return self.subject |
所々、djangoのテンプレートがありますが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 |
{% extends 'inquiry_apps/base.html' %} {% block page_title %}Inquiry list{% endblock %} {% block header %} {% endblock %} {% block content %} <h1>Inquiry list</h1> <form method="get"> <ul class="search_inquiry_form"> <li> <label for="id">Id:</label> <!-- <input type="number" name="id" min="1"> --> <span class="input_field">{{ form.id }}{{ form.id.errors }}</span> </li> <li> <label for="">Email:</label> <!-- <input type="text" name="email"> --> <span class="input_field">{{ form.email }}{{ form.email.erros }}</span> </li> <li> <label for="">Word:</label> <!-- <input type="text" name="message"> --> <span class="input_field">{{ form.word }}{{ form.word.erros }}</span> </li> <li> <label for=""></label> <span>※Word can find subject and message</span> </li> <li> <label for=""></label> <button type="submit" class="btn">Search</button> </li> </ul> </form> <hr> {% for num in inquiries_page.paginator.page_range %} {% if inquiries_page.number == num %} <span>{{ num }}</span> {% else %} <a href="?email={{ search_email }}&page={{ num }}">{{ num }}</a> {% endif %} {% endfor %} <table class="inquiry_list_table"> <thead> <tr> <td>Id</td> <td>Name</td> <td>Subject</td> <td>Message</td> <td>Email</td> <td>Updated</td> <td>Detail</td> <td>Status</td> </tr> </thead> <tbody> {% for inquiry in inquiries_page %} <tr> <td>{{ inquiry.id }}</td> <td><div class="container"><span>Name</span><span>{{ inquiry.name }}</span></div></td> <td><div class="container"><span>Subject</span><span>{{ inquiry.subject }}</span></div></td> <td><div class="container"><span>Message</span><span>{{ inquiry.message }}</span></div></td> <td><div class="container"><span>Email</span><span>{{ inquiry.email }}</span></div></td> <td><div class="container"><span>Updated</span><span>{{ inquiry.updated_at|date:"Y-m-j H:i " }}</span></div></td> <td><div class="container"><span>Detail</span><span><a href="{% url 'inquiry_apps:comment_list' inquiry.id %}">detail</a></span></div></td> <td> <div class="container"><span>Status</span><span> {% if inquiry.inquiry_status == inquiry.InquiryStatus.Pending %} Penging {% elif inquiry.inquiry_status == inquiry.InquiryStatus.Ignore %} Ignore {% else %} Competed {% endif %} </span></div> </td> </tr> {% endfor %} </tbody> </table> {% endblock %} |
クエリ後が引き継がれるようにviewsに設定
クエリを引き継がせるには、新たに_some_page_hrefという内部関数を作成します。明示的に内部関数は先頭にアンダーバーをつけます。
_some_page_href関数で返される戻り値( return ‘?’ + ‘&’.join(params) が、クエリで検索語の文字列をページングを実行しても引き継がれるようにしています。
views.py
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 |
def _some_page_href(id, email, current_page, word): params = [] if id: params.append('id=%s' % urllib.parse.quote(str(id))) if email: params.append('email=%s' % urllib.parse.quote(email)) if current_page: params.append('page=%s' % urllib.parse.quote(str(current_page))) if word: params.append('word=%s' % urllib.parse.quote(word)) return '?' + '&'.join(params) @require_http_methods(['GET']) def inquiry_list(request): qs = Inquiry.objects.order_by('-updated_at') form = InquiryFindForm(request.GET) if not form.is_valid(): return render(request, 'inquiry_apps/inquiry_list.html', {'form': form}) if form.is_valid(): id_int = form.cleaned_data['id'] email_str = form.cleaned_data['email'] page_int = form.cleaned_data['page'] word_str = form.cleaned_data['word'] if email_str is not None: qs = qs.filter(email__contains=email_str) if id_int is not None: qs = qs.filter(id=id_int) if word_str is not None: qs = qs.filter(Q(subject__contains=word_str) | Q(message__contains=word_str)) # making next page parameter next_page_href = _some_page_href(id_int, email_str, page_int+1, word_str) # making prev page parameter prev_page_href = _some_page_href(id_int, email_str, page_int-1, word_str) paginator = Paginator(qs, 5) try: inquiries_page = paginator.page(page_int) except EmptyPage: raise Http404('Page does not exist') context = { 'form': form, 'inquiries_page': inquiries_page, 'search_email': email_str, 'next_page_href': next_page_href, 'prev_page_href': prev_page_href, } |
htmlには以下のテンプレートを追記します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{% if inquiries_page.has_previous %} <a href="{{ prev_page_href }}">◀︎Prev</a> {% endif %} {% for num in inquiries_page.paginator.page_range %} {% if inquiries_page.number == num %} <span>{{ num }}</span> {% else %} <a href="?email={{ search_email }}&page={{ num }}">{{ num }}</a> {% endif %} {% endfor %} {% if inquiries_page.has_next %} <a href="{{ next_page_href }}">Next▶︎</a> {% endif %} |
スクールを利用して本格的に学ぶ
いかがでしたでしょうか?
今回10人中9人が挫折すると言われるプログラミングを、半年間もの間頑張れ、結果、今はPythonエンジニアとして働く事ができているのも、プログラミングスクールを利用して自ら目標を設定して講師の言う通りにひたすら打ち込んだまでです。
挫折率が高いプログラミングこそお金を払ってメンターを付けて、道を見失わないように環境を構築する必要があるのではないでしょうか。
結局一人だとどうしてもだらけてしまいます。
これはダイエットで自分一人では痩せられないけど、トレーナーを付けて否が応でもせざるを得ない環境を作ると一緒ですね。
ヒロヤンもプログラミング勉強開始直後はあれこれ悩みましたが、悩むよりも手っ取り早くテックキャンプやDMM Web Campのスクールに登録した方が最短ルートで勉強できるのではないかと考えました。