7 データべースの検索
7.1 GETでの受け取り
検索のようなデータベースの変更をともなわない処理は通常はPOSTでは作らずGETで作ります。なぜなら、GETでの送信は入力内容がURLに含まれるからです。これにより、再度そのURLにアクセスすれば検索結果が再現できます。
slist.html に検索フォームをmethod="get"で作成します。
<form action="/search" method="get" >
<input type="text" name="word">
<input type="submit" value="検索">
</form>
検索語を入力するテキストボックスにwordという名前を付けています。 hanbai.pyのほうではこれを/searchで受け取るsearch関数を作成します。
@app.get('/search')
def search():
word = request.args['word']
rows = Shouhin.query.filter(Shouhin.sname.contains(word)).all()
return render_template('hanbai/slist.html', rows=rows)
GETでの送信のため、検索語を受け取っているところを request.form ではなく request.args で行っています。
Shouhin.query.filter(条件).all() で条件を指定しての検索が可能です。条件式では列名を クラス名.列名 のように書きます。Shouhin.snameは文字列なので、これがwordを含んでいるかをcontainsメソッドで調べています。
検索結果を slist.html で表示します。
これによりGETでの検索が出来ます。例えば検索語に「ご」を入力したら、検索結果のURLは以下のようになるはずです。
http://127.0.0.1:5000/search?word=ご
このようにURLに入力内容を含めて送信するのがGETです。このため、GETでフォームを作成した場合、その入力内容をURLで再現することが出来ます。これにより、結果の共有や再現が容易になります。
通常、検索などのデータベースの変更を行わない処理はGETで作成します。しかし、パスワードの入力など、容易に再現されては困るものはPOSTで行います。
7.2 検索語の反映
現状では何を入力してこの検索結果になったかが、わかりません。そこで、hanbai.pyのsearch関数から、検索語をword=wordで送るようにします。
return render_template('hanbai/slist.html', rows=rows,word=word)
これを検索フォームに反映させます。name="word"のテキストボックスにvalue属性でwordを表示するようにします。
<input type="text" name="word" value="{{word}}" >
7.3 検索結果が無い場合
現状では検索結果が0件の場合、表が見出しだけ出てきて何が起こったかがよく分からない状態です。検索結果が0件の場合、表の見出しは表示せず、代わりに「検索対象がありません」などのメッセージを表示した方が良いでしょう。
このように条件によって表示を切り替えるためにテンプレート内でif文を使うことが出来ます。{% if 条件式 %}でif文を書きます。書き方はpythonと基本的には同じですが、条件式やelseの終わりに: は書かず、最後に endif を書きます。
{% if rows %}
<table>
<tr><th>商品ID</th><th>商品名</th><th>単価</th></tr>
{% for r in rows %}
<tr>
<td>{{r.sid}}</td>
<td>{{r.sname}}</td>
<td>{{r.tanka}}</td>
<td><a href="/uriage/{{r.sid}}">売上</a></td>
<td><a href="/del/{{r.sid}}">削除</a></td>
<td><a href="/update/{{r.sid}}">変更</a></td>
</tr>
{% endfor %}
</table>
{% else %}
<p>検索対象がありません</p>
{% endif %}
7.4 結合して検索
現在の売上一覧 /ulist では商品IDは表示されていますが、商品名は表示されていません。なぜなら売上テーブルには商品IDはあっても商品名は無いからです。商品名は商品テーブルにあります。
そこで、売上テーブルと商品テーブルを結合して表示してみます。
結合を行う場合、まず一対多の「一」の方でモデルクラス内にて db.relationship を記述します。今回はShouhinクラスに以下を記述します。
uriage = db.relationship('Uriage', backref='shouhin', lazy=True)
これは、Shouhinクラス内にUriageのリストを持つ、という意味です。このとき、backrefを設定することでUriageのクラスの方でもshouhinという名前で参照できるようになります。
また、Uriageクラスではsidの設定のところに db.ForeignKey('shouhin.sid') を追加します。これはsidが外部キーとして使われることを意味します。sidが同じshouhinテーブルを参照できるようになります。
sid = db.Column(db.Integer, db.ForeignKey('shouhin.sid'))
これにより、Uriageクラス内でsidが同じ Shouhin を参照できるようになりました。
ulist.html で結果を表示します。まず、見出しに商品名と単価を加えます。
for文ですが、rowsから取り出した結果は、UriageとShouhinの両方を返します。そこで、最初を既存の変数 r とし、次に s を追加します。
<tr><th>売上ID</th><th>商品ID</th><th>個数</th><th>日付</th><th>商品名</th><th>単価</th></tr>
そして、商品名は r.shouhin.sname で表示します。単価はr.shouhin.tankaです。
<td>{{r.shouhin.sname}}</td>
<td>{{r.shouhin.tanka}}</td>