🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

SQLAlchemy

SQLAlchemyとはPython 用のORMライブラリです。MIT Licenceのオープンソースとして提供されています。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

Q&A

解決済

1回答

4175閲覧

WebアプリでLIKE句を用いた検索がうまくできない

退会済みユーザー

退会済みユーザー

総合スコア0

Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

SQLAlchemy

SQLAlchemyとはPython 用のORMライブラリです。MIT Licenceのオープンソースとして提供されています。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

0グッド

0クリップ

投稿2019/10/23 12:34

編集2019/10/25 03:42

前提・実現したいこと

FlaskとPostgres, SQLAlchemnyで作ったWebアプリで
データの入力と検索を行い、検索は部分一致で結果を出してページに表示しようとしています。
SQLAlchemyのLIKE句を使った記述は以下の記事を参考にしました。
参考記事

発生している問題・エラーメッセージ

データの入力はフォームに入力したものがデータベースに正しく格納されていますが、
検索で結果をWebアプリで取得できない状態です。

エラーメッセージは出ていないので、以下の状況を
Postgresデータベース内でLIKE句を使ってSELECTで求めた結果と同じ出力にするにはどうすればいいでしょうか。

htmlファイルの

{% for name in results %} <li> <h5>{{ todo.name }}</h5> <!-- 検索結果をページにプリント --> </li> {% endfor %}

が機能しておらず、Searchをクリックすると以下が表示されます。

{ "city": "", "name": "" }

該当のソースコード

Postgres データベース内

# \dt List of relations Schema | Name | Type | Owner --------+-------+-------+------- public | todos | table | username # select * from todos; id | name | city ----+--------+------- 1 | Hina | Tokyo 2 | Shun | Tokyo 3 | Taro | Fukuoka 4 | Rika | Nagano 5 | Hanako | Tokyo and Yokohama 6 | Haruka | Kyoto

app.py

python

1from flask import Flask, render_template, request, redirect, url_for, abort, jsonify 2from flask_sqlalchemy import SQLAlchemy 3import sys 4 5app = Flask(__name__) 6app.config['SQLALCHEMY_DATABASE_URI'] = 'postgres://username@localhost:5432/simplewr' 7db = SQLAlchemy(app) 8 9class Todo(db.Model): 10 __tablename__ = 'todos' 11 12 id = db.Column(db.Integer, primary_key=True) 13 name = db.Column(db.String) 14 city = db.Column(db.String(120)) 15 16 def __repr__(self): 17 return f'<Todo {self.id} {self.name} {self.city}>' 18 19db.create_all() 20 21@app.route('/todos/create', methods=['POST']) 22def create_todo(): 23 error = False 24 body = {} 25 26 try: 27 name = request.form['name'] 28 city = request.form['city'] 29 todo = Todo(name=name, city=city) 30 db.session.add(todo) 31 db.session.commit() 32 body['name'] = todo.name 33 body['city'] = todo.city 34 except: 35 error = True 36 db.session.rollback() 37 print(sys.exc_info()) 38 finally: 39 db.session.close() 40 if error: 41 abort (400) 42 else: 43 return jsonify(body) 44 45@app.route('/todos/search', methods=['POST']) 46def search_venues(): 47 #HTMLのフォームから検索に用いられた単語を取得 48 search_term = request.form.get('search_term'); 49 results = [] 50 # 部分一致 LIKE 51 # WHERE LIKE '%...%' 52 if search_term == "": 53 print("enter a keyword") 54 55 for student in session.query(Todo).filter(todo.city.like('%search_term%')): 56 print(todo.name) 57 results.append(todo.name) 58 59 """ 60 Tokyoで検索された時 61 Todo.cityがTokyo and Yokohamaでも、Tokyoでも部分一致で該当するデータのnameを出力 62 63 出力 64 Hanako 65 Taro 66 67 """ 68 return render_template('index.html', results=results, search_term=search_term) 69 #htmlのrenderにはjinja2というものが使われており、中でPythonのコードを実行することができる 70 71@app.route('/') 72def index(): 73 return render_template('index.html', data=Todo.query.all())

index.html
(app.pyと同じフォルダにtemplatesというフォルダを作成し、index.htmlを置いています。

html

1<html> 2<head> 3 <title>Todo App</title> 4<style> 5 .hidden{ 6 display: none; 7 } 8</style> 9</head> 10<body> 11 <form method="post" action="/todos/create"> 12 <h4>name</h4> 13 <input type= "text" name="name" /> 14 <h4>city</h4> 15 <input type= "text" name="city" /> 16 <input type= "submit" value="Create" /> 17 <h4>search box</h4> 18 <input type="text" name="search_term" /> 19 <input type= "submit" value="Search" /> 20 </form> 21 <div id= "error" class="hidden">Something went wrong!</div> 22 <ul> 23 {% for d in data %} 24 <li>{{d.name}}</li> 25 <li>{{d.city}}</li> 26 <li>{{d.search_term}}</li> 27 {% endfor %} 28 {% for name in results %} 29 <li> 30 <h5>{{ todo.name }}</h5> <!-- 検索結果をページにプリント --> 31 </li> 32 {% endfor %} 33 </ul> 34 <script> 35 const nameInput = document.getElementById('name'); 36 const cityInput = document.getElementById('city'); 37 const sechInput = document.getElementById('search_term'); 38 document.getElementById('form').onsubmit = function(e) { 39 e.preventDefault(); 40 const name = nameInput.value; 41 const city = cityInput.value; 42 const search_term = sechInput.value; 43 descInput.value = ''; 44 fetch('/todos/create', { 45 method: 'POST', 46 body: JSON.stringify({ 47 'name': name, 48 'city': city, 49 }), 50 headers: { 51 'Content-Type': 'application/json', 52 } 53 }) 54 55 //return the serched data 56 SQL 57 SELECT city from table where LIKE search_term; 58 59 .then(response => response.json()) 60 .then(jsonResponse => { 61 console.log('response', jsonResponse); 62 li = document.createElement('li'); 63 li.innerText = name; 64 li.innerText = city; 65 document.getElementById('todos').appendChild(li); 66 document.getElementById('error').className = 'hidden'; 67 }) 68 .catch(function() { 69 document.getElementById('error').className = ''; 70 }) 71 } 72 </script> 73</body> 74</html>

上記のプログラムを

$ FLASK_APP=app.py FLASK_DEBUG=true flask run

で実行します。

試したこと

SQL文で取得できることは確認しました。

# select name from todos where city LIKE 'Tokyo'; name ------- Hina Shun Hanako

###ご回答を受けての追記

不明瞭な点があったため明白にします。

  • 実現したいこと

検索フォームに入力されたら、大文字小文字問わず部分一致で該当するcityを持つデータのnameを取得し、画面遷移で(POSTして)表示を変えたいです。

Tokyoの場合、画面遷移をして

Hanako Taro

です。

  • search_venuesの戻り値がrender_template

に関して、javascriptとpythonのjinja2で表示処理のちがいがよくわかっていないのですが
検索しても該当するページを見つけられていない状況です。

def create_todo():に倣って以下のように変更しましたが依然として、

  1. 部分一致検索が機能しているのか
  2. javascriptとpythonのjinja2のちがいによる表示処理

がうまく理解できていないので、最新版のコードでもエラーが出ています。

python

1 2@app.route('/todos/search', methods=['GET']) 3def search_venues(): 4 #HTMLのフォームから検索に用いられた単語を取得 5 search_term = db.session.query(Todo).get(request.form["search_term"].strip()) 6 #search_term = request.form.get('search_term'); 7 results = [] 8 9 if search_term == "": 10 print("enter a keyword") 11 12 users = session.query(Todo).filter(Todo.city.like('%'+ search_term + '%')).all() 13 for user in users: 14 print(user) 15 results.append(user) 16 17 return jsonify(results)

補足情報(FW/ツールのバージョンなど)

Python 3.6.0
Flask 1.1.1
Sqlalchemy 1.3.10
psql (PostgreSQL) 11.5
ブラウザ Google Chrome

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

FiroProchainezo

2019/10/24 05:23

> Searchをクリックすると以下が表示されます。 Searchが見当たらないのですが、どこにあるのでしょうか? ボタンですか?関数呼び出しかなんかですか??? 他の質問にも書きましたが、このプログラムは最新版ですか?
退会済みユーザー

退会済みユーザー

2019/10/24 09:06

すみませんでした。Searchはボタンです。最新版に直しました。
guest

回答1

0

ベストアンサー

確認しましたが、このアプリ、動いているのか疑問を感じるレベルでおかしいです。

以下を確認いただけますか?

app.py

  • sessionがimportされていないのに、sessionを使っている(search_venues)

DBにアクセスしたいなら、db.session.query()としてください。

  • search_venuesのtodoが小文字

Todoは大文字ですよね?

  • for student in session.query(Todo).filter(todo.city.like('%search_term%')):

以下のようにしないとずっと固定値なのでは?

python

1Todo.city.like('%'+ search_term + '%')

containsを使っても良いかもしれません。

python

1Todo.city.contains(search_term)
  • search_venuesの戻り値がrender_template

fetchで値を取得したいならrender_templateを使うのは変です。
jsonとかのデータを返し、fetch(javascript)の処理で画面に反映しましょう。
その場合、python側の処理ではなくなるので、jinja2での表示はできません。

javascriptで表示処理したいのか、pythonのjinja2で表示処理をしたいのかを決めて、それにあった作り方をしましょう。

index.html

  • create, searchボタン、どちらも送信先がcreate_todoになっている。
<form>のactionを別にしないと処理は別になりません。
  • fetchが動いているように見えない。

たぶん、普通にPOSTしているだけになっています。(fetchしていたら、どちらのボタン(search, create)を押してもcreate_todoに行くはずですが、formを分けたら送信先が変わったので。

何がしたいのかよくわからなかったのでこのくらいしか回答できませんが、index.html(/)をfetchを使って遷移せずに書き換えたいのか、画面遷移で(POSTして)表示を変えたいのかを明確にすればもう少し変わると思います。

追記(2019/10/25 20:21)

最初に、すべてに指摘は入れているわけではないので、指摘されていないところが問題無いとは思わないでください。

検索フォームに入力されたら、大文字小文字問わず部分一致で該当するcityを持つデータのnameを取得し、画面遷移で(POSTして)表示を変えたいです。

画面遷移してで良いなら、formのpostにsearch_venuesを指定(<form method="post" action="{{ url_for('search_venues') }}">)し(フォームを2つに分けました)、
index.htmlではなく、適当にsearch.html等を作成し、
app.pyのsearch_venuesの処理を変更して、render_templateを返すように
すればOKと思います。

index.html

html

1 <form method="post" action="/todos/create"> 2 <h4>name</h4> 3 <input type= "text" name="name" /> 4 <h4>city</h4> 5 <input type= "text" name="city" /> 6 <input type= "submit" value="Create" /> 7 </form> 8 <form method="post" action="{{ url_for('search_venues') }}"> 9 <h4>search box</h4> 10 <input type="text" name="search_term" /> 11 <input type= "submit" value="Search" /> 12 </form> 13

search.html

html

1<!DOCTYPE html> 2<html lang="ja"> 3<head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6</head> 7<body> 8 <div> 9 <ul> 10 {% for d in ret %} 11 <li>{{d.name}} {{ d.city }}</li> 12 {% endfor %} 13 </ul> 14 </div> 15</body> 16</html>

search_venues

python

1@app.route('/todos/search', methods=['POST']) 2def search_venues(): 3 search_term = request.form.get('search_term') 4 ret = db.session.query(Todo).filter(Todo.city.contains(search_term)).all() 5 return render_template('search.html', ret=ret)

jinja2とjavascriptについて

search_venuesの戻り値がrender_template
に関して、javascriptとpythonのjinja2で表示処理のちがいがよくわかっていないのですが
検索しても該当するページを見つけられていない状況です。

pythonとjinja2はサーバ側で動作します。

javascriptは、サーバ側が処理し終わって、ブラウザにすべてが表示されたあとに動作します。(必ずではありませんが)

ブラウザでアクセス(localhost:5000)したら以下のような流れだと思ってください。

  1. ブラウザでhttp://localhost:5000/を開く
  2. app.pyのindexが動作する。
  3. render_templateでindex.htmlが呼ばれる。
  4. index.htmlのマスタッシュ記法{{}}部分をpython(jinja2)が処理する。
  5. index.htmlがブラウザに表示される。({{}}は残っていない)
  6. index.htmlのscriptに記載されている部分が動作する。(このソースでは動作する場所がありませんが)

fetchの結果を{{}}に反映しようとしているのは、処理している部分が違うので無理です。
Chromeでlocalhost:5000を表示し、開発者ツール(F12)を表示してみてください。(Sourcesタブの左側top->localhost:5000->)(index)
{{}}で囲まれたものはSorucesに見当たらないはずです。

python(flask, jinja2)はpython, javascriptはjavascriptで処理する必要があります。

html

1# これは無理です。 2 {% for name in results %} 3 <li> 4 <h5>{{ todo.name }}</h5> <!-- 検索結果をページにプリント --> 5 </li> 6 {% endfor %}

余談

index.htmlに以下の記述がありますが、Javascirptの記述として正しく無いので削除した方が良いです。

html

1 //return the serched data 2 SQL 3 SELECT city from table where LIKE search_term;

投稿2019/10/25 03:00

編集2019/10/25 11:22
FiroProchainezo

総合スコア2421

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/10/25 03:42

ご回答いただきありがとうございます。 ご回答に関して詳細なレスポンスは質問に追記いたしました。 また、質問中のコードを最新版のコードに書き換えてもいいでしょうか。
退会済みユーザー

退会済みユーザー

2019/10/25 12:53

ご丁寧にありがとうございます。コメントアウトが抜けており申し訳ございませんでした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問