質問をすることでしか得られない、回答やアドバイスがある。

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

ただいまの
回答率

87.49%

【python+mysql】select文のwhere句に、動的に変化する単語を挿入する書き方を教えてください。

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,175

score 36

丸一日、試行錯誤していたのですが、どうしても上手く行かないので、教えてください。

1.<やりたい事>
pythonで、Mysql DBからデータを取得し、それをHTMLに表示しようとしております。
DBは、下記の様になっております。
イメージ説明

HTML上で、下記様にチェックを入れて、送信ボタンを押すと、
イメージ説明

この様に表示したいと思っております。
イメージ説明

2.<質問>
Pythonコードは項番3に記載しておきますが、下記1)の様に、チェックされた項目(国名)は、namesと言う変数で取得しております。

1)names = request.form.getlist('checkbox')

2)ここで、print(names) とすると、想定通り、チェックされたのを返します。
(想定通りです。)
>['Afgahanistan', 'Australia', 'France']

3)for name in names: 
<tab> print(name) 
また、この様にすると、国名を個別に返してきます。(これも想定通りです。)
>Afgahanistan
>Australia
>France

4)国名(この場合は、上記3か国)をSelect文のwhere句に代入して、変数として、情報を取りたいと考え、下記(a)の様にSelect文を書いたのですが、これが動作しません。
(a)  sql = "select Country, Agency, email from tb3 where Country='name'"; 
(このsql文をPythonに書くと、エラーは出ないのですが、出力が真っ白な画面となります。)

尚、問題の切り分けの為に、このsql文のWhere句を下記の様(b)に個別国名(この例では、Afgahanistan)に書き換えると、Afgahanistanのデータを正常に返してきます。ついては、やはり、上記(a)の'name'の部分がオカシイのでは?と思っております。
(b)  sql = "select Country, Agency, email from tb3 where Country='Afgahanistan'"; 

繰り返しになるのですが、多分、(a)の「where Country='name';」のnameの部分で、変数がキチンと取得できていないと思うのですが、如何でしょうか?

3.下記にPythonコードを記載します。
また、本件とは関係ないと思いますが、一応、HTMLのコードも記載しておきます。
よろしくお願いいたします。

<Pythonのコード>

from flask import Flask, render_template,request
import pymysql
app = Flask(__name__)

@app.route('/', methods=['GET'])
def get():
    return render_template('index1.html', \
        title = 'Form Sample(get)', \
        message = 'Where do you want to go?')


def getConnection():
return pymysql.connect(
host='localhost',
db='first_db',
user='root',
password='yireozna',
charset='utf8',
cursorclass=pymysql.cursors.DictCursor
)


@app.route('/', methods=['POST'])
def select_sql():
    connection = getConnection()
    message = "test"
    names = request.form.getlist('checkbox')
    print(names)

    for name in names:
        print(name)

    <tab> sql = "select Country, Agency, email from tb3 where Country='name'";
    <tab> cursor = connection.cursor()
    <tab> cursor.execute(sql)
    <tab> listx1s = cursor.fetchall()

    <tab> cursor.close()
    <tab> connection.close()

    <tab> return render_template( 'index2.html', listx1s = listx1s)

if __name__ == '__main__':
    app.run()
コード

<Index1.htmlのコード>

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Hello World</title>
  </head>
  <body>
    <h1>{{ title }}</h1>
    <p>{{ message }}</p>
    <form action="/" method="POST" enctype="multipart/form-data">
        <div>
            <label for="ck1">Afgahanistan:</label>
            <input type="checkbox" id="ck1" name="checkbox" value="Afgahanistan">
        </div>
        <div>
            <label for="ck2">Australia:</label>
            <input type="checkbox" id="ck2" name="checkbox" value="Australia">
        </div>
        <div>
            <label for="ck3">England:</label>
            <input type="checkbox" id="ck3" name="checkbox" value="England">
        </div>
        <div>
            <label for="ck4">France:</label>
            <input type="checkbox" id="ck4" name="checkbox" value="France">
        </div>
        <div>
            <input type="submit" value="送信">
        </div>
    </form>
  </body>
</html>

<Index2.htmlのコード>

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Hello World</title>
  </head>
  <body>
    <h1>{{ title }}</h1>    
    <p>
    {% block content %}
        {% for listx1 in listx1s %}
            <p>{{listx1}}</p>
        {% endfor %}

    {% endblock %}
    </form>
  </body>

</html>
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+2

SQLのWHERE IN句およびパラメータ引数を用いたコード例です。
DB関連の最小限のコードサンプルとなっています。
なお、プレースホルダの文字はDB(ドライバ)依存なのですがmysqlでは?ではなく%sのようですので適宜修正ください。
参考:mysqlclientのプレースホルダでIN句を使う

import sqlite3

con = sqlite3.connect('test.db')
cursor = con.cursor()

for names in [[],['Japan'],['Japan','England']]:

    if not names:
        continue

    holder = ','.join(['?' for _ in names]) # mysqlは'%s'かも
    sql = f'select Country from tb3 where Country in ({holder})'
    print(sql)
    cursor.execute( sql, names)
    for line in cursor.fetchall():
        print(line)

cursor.close()
con.close()

#select Country from tb3 where Country in (?)
#('Japan',)
#select Country from tb3 where Country in (?,?)
#('England',)
#('Japan',)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/04/26 20:51 編集

    これ思いつきませんでした……その手があったか……

    キャンセル

  • 2021/04/26 21:23

    ちょっと泥臭いですが…
    プレースホルダ部分を可変で指定する方法を検索しても意外とひっかかってこなくて
    スマートな方法がないんですよね。

    キャンセル

  • 2021/04/28 00:16

    詳しい御説明を、ありがとうございました。
    御回答を頂いてから、丸一日以上、色々と試行錯誤したのですが、上手くいきません。
    私自身がプレースホルダ―をきちんと理解できていないのが原因です。
    もう少し、調べてみます。取り急ぎ、お礼まで。。。

    キャンセル

  • 2021/04/28 17:16

    頂いた御回答とリンク記事を熟読し、内容は私なりに理解致しました。
    大変、勉強になりました。
    ありがとうございました。

    ただ、実装に適用しようとすると、どうしても上手くいきません。(これだけ情報を頂ければ、解決できるだろうと思って、「解決済」には、してしまったのですが。。。)
    とは言いつつも、分からない部分を、大幅に狭める事は出来たので、この質問は一旦、クローズと致します。問題を切り分けた上で、別の質問(もっと端的な質問)を立てますので、もし可能で御座いましたら、そちらにもコメントを頂きたく。
    重ねて、ありがとうございました。

    キャンセル

checkベストアンサー

+1

プレースホルダという機能を使います。

    sql = "select Country, Agency, email from tb3 where Country=%s";
    cursor.execute(sql, (name, ))

これで意図通り動くのかどうかはわかりません(ループの最後に見たnameでしか取れないはず)。そちらはご自身でどうにか直してください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/04/26 18:03

    ありがとうございます。
    色々と御意見を頂いたり、試行錯誤する中で、「何が分からないのか?」が分かってきました。
    SQL文の一般的な事だと思いますが、一つ教えてください。

    <ご説明>
    1.
    pymysqlでSQL文を書く際に、
    (a) sql="select Country, Agency, email from tb3 where Country ='England'";
    と書けば、勿論、正しい値を返します。

    2.
    しかしながら、
    print(" ' "+names[1]+" ' ")
    >'England'
    と返してくる状態で、(b)の様に書くと、(a)と同じ様に動作しません。
    (b) sql="select Country, Agency, email from tb3 where Country =" ' "+names[1]+" ' "";
    (因みに、ここでprint(sql)を実行すると、「" ' "+names[1]+" ' "」が、’England’に変換されないで、そのまま表示されます。)

    <御質問>
    (b)のwhere句に、names[1]をセットするには、どういう書き方になるのでしょうか?

    基本的な事で恐縮です。
    ただ、ここをクリアすれば、後は、解決できると思っております。
    よろしくお願いいたします。

    キャンセル

  • 2021/04/26 20:55

    プレースホルダを使うのであれば、
    sql = "select Country, Agency, email from tb3 where Country=%s"
    cursor.execute(sql, (names[1], ))
    という感じでやります。
    「文字列でSQLを組み立てるのはSQLインジェクションあるから駄目」という話はちょっとググれば無限に出てきますが、敢えてやるなら
    "select Country, Agency, email from tb3 where Country ='" + names[1] + "'"
    でしょうか。

    キャンセル

  • 2021/04/26 21:20

    ありがとうございます。
    お陰様で、やっと大枠を理解しました。
    確かに、私のやろうとしている事は、「SQLインジェクション」ですね。(言われてみて、初めて気が付きました。) 
    従って、SQLインジェクションの「記法」については、情報があまりネットになく、出てくるのは、プレースホルダ―の話と言う事になるのですね。
    誠に有難うございました。私の頭の中で、話が繋がりました。
    ベストアンサーとさせて頂きます。

    キャンセル

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

  • ただいまの回答率 87.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る