プログラミング > Python >

フォーム (Webページ内の検索)

2023-01-21

目次

フォームとは

   フォームとはWebページに訪問した人が入力するための欄のこと。ここでは訪問者にキーワードを入力してもらい、今まで登録したWebページの中からキーワードに一致する内容のページを一覧にして表示する。そして表示されたページを選ぶとそのページに移動できるようにする。
   一つ一つのページをhtmlで列挙していくのは骨が折れる。しかし、すでにモデルを使ってページを登録しているため少しの手間で表示することができる。

フォームの作成

   まずフォームを作るためのURLを設置する。urls.pyに以下を記入する。
コード1.
/ProjectName/webpage/urls.py
                    
from django.urls import path
from . import views

app_name = 'webpage' urlpatterns = [ path('', views.index, name='index'), path('category/', views.category, name='category'), path('category/<slug:title>/', views.title, name='title'), ]
   次にフォームクラスを実装する。ProjectNmae/webpage/にforms.pyというファイルを作り以下のようにWebpageFormクラスを導入する。フォームにはキーワードの入力フィールドとしてform_keywordform_keyword2の2つを用意した。
コード2.
/ProjectName/webpage/forms.py
                    
from django import forms

class WebpageForm(forms.Form): form_keyword = forms.CharField(label='キーワード', max_length=150, required=False) form_keyword2 = forms.CharField(label='キーワード2', max_length=150, required=False)
requiredによってフォームの入力が必須かどうかを決められる。
   urls.pyviews.pycategory関数で表示の制御を行うようにした。views.pyを以下のようにする。
コード3.
/ProjectName/webpage/views.py
                    
from django.core.paginator import Paginator
from django.shortcuts import render

from .models import Webpage from .forms import WebpageForm
def index(request): return render(request, 'index.html')
def category(request): form_set = WebpageForm() model = Webpage.objects.all() context = { 'form_set': form_set, 'models': model } return render(request, 'category.html', context)
def title(request, **kwargs): model = Webpage.objects.filter(webpage_url=kwargs['title']).first() if model is not None: context = { 'title': model.webpage_title, 'contents': model.webpage_contents, } return render(request, 'title.html', context)
model = Webpage.objects.all()で登録したすべてのページを呼び出している。
   そしてcategory.htmlで実際に表示内容を記述する。
コード4.
/ProjectName/webpage/templates/category.html
                    
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<title>Webページ 検索</title>
</head>
<body>
<div style="text-align: center;">
	<h1>Webページの検索</h1>
</div>
<form action="" method="post">
	{% csrf_token %}
	<div>
		{% for field in form_set %}
			{{ field.errors }}
			<p>{{ field.label_tag }} {{ field }}</p>
		{% endfor %}
	</div>
	<button type="submit">検索</button>
</form>
<hr>
<p>Webページ一覧</p>
<hr>
<hr>
{% for model in models %}
	<div>
		<h4><a href="{% url "webpage:title" model.webpage_url %}">{{ model.webpage_title }}</a></h4>
		<p>{{ model.webpage_contents }}</p>
	</div>
	<hr>
{% endfor %}
</body>
</html>
                
contextによってform_setmodelsがhtml内で使えるようになっている。form_setからは2つの入力欄をfor文によって順に取り出して表示している。modelsも同様にfor文を回し登録したページを取り出している。{% ... %}はdjangoの特殊なhtml記法。
   ターミナルでpython manage.py runserverとして「http://127.0.0.1:8000/webpage/category/ 」にアクセスすると、フォームとページ一覧が表示される。
図1. フォームによる入力欄とWebページ一覧を表示している。

ページネーション

   登録したページが多くなると下に長くページが続き、とても見づらくなる。そこでページネーションと言われるものを使うとページを複数に区切ることができる。views.pycategory関数を以下のようにする。
コード5.
/ProjectName/webpage/views.py
                    
def category(request):
	form_set = WebpageForm()
	model = Webpage.objects.all()
	paginator = Paginator(model, per_page=3)
	page_number = request.GET.get('page')
	model = paginator.get_page(page_number)
	context = {
		'form_set': form_set,
		'models': model
	}
	return render(request, 'category.html', context)
                
   Paginatorを使ってper_page = 3としているように登録したWebページを3つずつ表示するように設定している。登録数が15なら15/3=5ページに区切られる。per_page = 5とすれば15/5=3ページに区切られる。区切られたページにアクセスできるようにするためcategory.htmlを以下のようにする。現在、前後、最初と最後のページを表示するようにしている。
コード6.
/ProjectName/webpage/templates/category.html
                    
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<title>Webページ 検索</title>
</head>
<body>
<div style="text-align: center;">
	<h1>Webページの検索</h1>
</div>
<form action="" method="post">
	{% csrf_token %}
	<div>
		{% for field in form_set %}
			{{ field.errors }}
			<p>{{ field.label_tag }} {{ field }}</p>
		{% endfor %}
	</div>
	<button type="submit">検索</button>
</form>
<hr>
<p>Webページ一覧</p>
<hr>
<hr>
{% for model in models %}
	<div>
		<h4><a href="{% url "webpage:title" model.webpage_url %}">{{ model.webpage_title }}</a></h4>
		<p>{{ model.webpage_contents }}</p>
	</div>
	<hr>
{% endfor %}
{% if models.has_next %}
	{% if models.number != 1 %}
		<a href="?page={{ models.previous_page_number }}">前のページ ({{ models.previous_page_number }})</a>
	{% endif %}
	<a href="?page={{ models.number }}">現在のページ ({{ models.number }})</a>
	<a href="?page={{ models.next_page_number }}">次のページ ({{ models.next_page_number }})</a>
	<a href="?page={{ models.paginator.num_pages }}">最後のページ &raquo; ({{ models.paginator.num_pages }})</a>
{% elif models.has_previous %}
	<a href="?page=1">最初 &laquo; (1)</a>
	<a href="?page={{ models.previous_page_number }}">前のページ ({{ models.previous_page_number }})</a>
	<a href="?page={{ models.number }}">現在のページ ({{ models.number }})</a>
{% endif %}
</body>
</html>
                
図2. 表示数が3つになり、次ページなどにアクセスできる。

フォームの送信 (GETとPOST)

   今のところフォームに記入して送信を押しても何も変化はないが、実は内部的には送信によって状態が変化している。そのため訪問者に記入してもらったフォームは一度送信され、送信されたフォームに基づいてページを書き換える仕組みがあれば良い。
   フォームが送信されていない状態は「GET」フォームが送信された状態は「POST」と呼ばれている。djangoにもGETかPOSTを判断する方法がある。POSTのときに入力欄にキーワードがあるはずなのでそれに基づいて一覧内容を書き換えるようにする。views.pycategory関数を以下のようにする。
コード7.
/ProjectName/webpage/views.py
                    
def category(request):
	form_set = WebpageForm()
	model = Webpage.objects.all()
	paginator = Paginator(model, per_page=3)
	page_number = request.GET.get('page')
	model = paginator.get_page(page_number)
	if request.method == 'POST':
		form_set = WebpageForm(request.POST)
		if request.POST['form_keyword'] != '':
			model = model.filter(webpage_contents__contains=request.POST['form_keyword'])
			if request.POST['form_keyword2'] != '':
				model = model.filter(webpage_contents__contains=request.POST['form_keyword2'])
	context = {
		'form_set': form_set,
		'models': model
	}
	return render(request, 'category.html', context)
                
request.methodによってGETかPOSTを判断し、POSTならrequest.POSTWebpageFormクラスの変数名をキーワードとしてフォームに記入された値がわかる。そのキーワードが空白でなければWebページの内容の中で一致するものを抽出している。