前提・実現したいこと
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():に倣って以下のように変更しましたが依然として、
- 部分一致検索が機能しているのか
- 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
回答1件
あなたの回答
tips
プレビュー