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

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

新規登録して質問してみよう
ただいま回答率
85.37%
Flask

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1255閲覧

flaskのdelete機能の追加につまづいております。(ドのつく初心者です)

takeko41

総合スコア12

Flask

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

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2023/09/30 22:12

編集2023/09/30 22:13

main.py

1from crypt import methods 2from turtle import title 3#db 追加 4from flaskr import app, db 5from flask import render_template, request, redirect, url_for 6 7from flaskr.db import DATABASE 8 9import sqlite3 10DATABASE = "database.db" 11 12 13@app.route('/') 14def index(): 15 con = sqlite3.connect(DATABASE) 16 db_books = con.execute("SELECT * FROM books").fetchall() 17 con.close 18 19 20 books = [] 21 for row in db_books: 22 books.append({"title": row[0], "price": row[1], "arrival_day": row[2]}) 23 24 return render_template( 25 'index.html',books=books 26 ) 27 28@app.route("/form") 29def form(): 30 return render_template("form.html") 31 32@app.route("/register", methods=["POST"]) 33def register(): 34 title = request.form["title"] 35 price = request.form["price"] 36 arrival_day =request.form["arrival_day"] 37 38 con = sqlite3.connect(DATABASE) 39 con.execute('INSERT INTO books VALUES(?,?,?)',[title,price,arrival_day]) 40 41 con.commit() 42 con.close() 43 return redirect(url_for("index")) 44 45

以上のコードに削除機能を追加したく質問させて頂きました。

main.py

1from crypt import methods 2from turtle import title 3#db 追加 4from flaskr import app, db 5from flask import render_template, request, redirect, url_for 6 7from flaskr.db import DATABASE 8 9import sqlite3 10DATABASE = "database.db" 11 12 13@app.route('/') 14def index(): 15 con = sqlite3.connect(DATABASE) 16 db_books = con.execute("SELECT * FROM books").fetchall() 17 con.close 18 19 20 books = [] 21 for row in db_books: 22 books.append({"title": row[0], "price": row[1], "arrival_day": row[2]}) 23 24 return render_template( 25 'index.html',books=books 26 ) 27 28@app.route("/form") 29def form(): 30 return render_template("form.html") 31 32@app.route("/register", methods=["POST"]) 33def register(): 34 title = request.form["title"] 35 price = request.form["price"] 36 arrival_day =request.form["arrival_day"] 37 38 con = sqlite3.connect(DATABASE) 39 con.execute('INSERT INTO books VALUES(?,?,?)',[title,price,arrival_day]) 40 41 con.commit() 42 con.close() 43 return redirect(url_for("index")) 44 45 46 47 48#以下追記した削除コードです 49@app.route('/delete/<int:id>', methods=['GET', 'POST']) 50def delete(id): 51 con = sqlite3.connect(DATABASE) 52 if request.method == 'POST': 53 con.execute('DELETE FROM books WHERE id = ?', [id]) 54 con.commit() 55 con.close() 56 return redirect(url_for('index')) 57 else: 58 book = con.execute('SELECT * FROM books WHERE id = ?', [id]).fetchone() 59 con.close() 60 if book: 61 return render_template('delete.html', book=book) 62 else: 63 return "Book not found", 404 64

db.py

1import sqlite3 2 3 4DATABASE = "database.db" 5 6def create_books_table(): 7 con = sqlite3.connect(DATABASE) 8 con.execute("CREATE TABLE IF NOT EXISTS books (title, price, arrival_day)") 9 con.close() 10 11 12

__init__.py

1from flask import Flask 2app = Flask(__name__) 3import flaskr.main 4 5from flaskr import db 6db.create_books_table()

index.html

1<!DOCTYPE html> 2<html lang="ja"> 3 4<head> 5 <meta charset="UTF-8"> 6 <title>sapu-app</title> 7 8 <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> 9</head> 10 11<body> 12 13 <h1>書店</h1> 14 <h2>今月の新刊一覧</h2> 15 {% if books == [] %} 16 <p>今月の新刊情報はまだありません</p> 17 {% else %} 18 <table border="1"> 19 <tr> 20 <th>入荷日</th> 21 <th>タイトル</th> 22 <th>金額</th> 23 </tr> 24 25 {% for book in books %} 26 <tr> 27 <td>{{ book.arrival_day }}</td> 28 <td>{{ book.title }}</td> 29 <td>{{ book.price }}円</td> 30 </tr> 31 {% endfor %} 32 </table> 33 {% endif %} 34 35 <a href= "{{ url_for('form') }}">編集</a> 36 37 38 39<!-- 削除用に追記したhtml --> 40 41 <table> 42 <thead> 43 <tr> 44 <th>Title</th> 45 <th>Price</th> 46 <th>Arrival Day</th> 47 <th>Actions</th> 48 </tr> 49 </thead> 50 <tbody> 51 {% for book in books %} 52 <tr> 53 <td>{{ book["title"] }}</td> 54 <td>{{ book["price"] }}</td> 55 <td>{{ book["arrival_day"] }}</td> 56 <td> 57 <a href="{{ url_for('delete', id=loop.index) }}">Delete</a> 58 </td> 59 </tr> 60 {% endfor %} 61 </tbody> 62 </table> 63 64 65 66 67 68</body> 69 70</html> 71 72

form.html

1<!DOCTYPE html> 2<html lang="ja"> 3 4<head> 5 <meta charset="UTF-8"> 6 <title>sapu-app</title> 7 8 <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> 9 10</head> 11 12<body> 13 <h1>書店</h1> 14 <form method="post" action="{{ url_for('register') }}"> 15 <table border="1"> 16 <tr> 17 <th>入荷日</th> 18 <th>タイトル</th> 19 <th>金額</th> 20 </tr> 21 <tr> 22 <td><input type="text" name="arrival_day"></td> 23 <td><input type="text" name="title"></td> 24 <td><input type="text" name="price"></td> 25 </tr> 26 </table> 27 <br> 28 <input type="submit" value="登録"> 29 </form> 30</body> 31 32</html> 33 34<!DOCTYPE html> 35<html lang="jp"> 36<head> 37 <meta charset="UTF-8"> 38 <title>Delete Book</title> 39</head> 40<body> 41 <h1>Delete Book</h1> 42 <p>Are you sure you want to delete the following book?</p> 43 <p>Title: {{ book[0] }}</p> 44 <p>Price: {{ book[1] }}</p> 45 <p>Arrival Day: {{ book[2] }}</p> 46 <form method="POST"> 47 <input type="submit" value="Delete"> 48 </form> 49</body> 50</html> 51 52

delete.html

1<!DOCTYPE html> 2<html lang="jp"> 3<head> 4 <meta charset="UTF-8"> 5 <title>Delete Book</title> 6</head> 7<body> 8 <h1>Delete Book</h1> 9 <p>Are you sure you want to delete the following book?</p> 10 <p>Title: {{ book[0] }}</p> 11 <p>Price: {{ book[1] }}</p> 12 <p>Arrival Day: {{ book[2] }}</p> 13 <form method="POST"> 14 <input type="submit" value="Delete"> 15 </form> 16</body> 17</html> 18 19

ローカルホストを立ち上げdeleteを押すと以下のエラーが出てきます。

OperationalError sqlite3.OperationalError: no such column: id Traceback (most recent call last) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/flask/app.py", line 2213, in __call__ return self.wsgi_app(environ, start_response) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/flask/app.py", line 2193, in wsgi_app response = self.handle_exception(e) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/flask/app.py", line 2190, in wsgi_app response = self.full_dispatch_request() File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/flask/app.py", line 1486, in full_dispatch_request rv = self.handle_user_exception(e) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/flask/app.py", line 1484, in full_dispatch_request rv = self.dispatch_request() File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/flask/app.py", line 1469, in dispatch_request return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) File "/Users/kojitakemura/Desktop/pythonProject1 2元本 2/flaskr/main.py", line 58, in delete book = con.execute('SELECT * FROM books WHERE id = ?', [id]).fetchone() sqlite3.OperationalError: no such column: id The debugger caught an exception in your WSGI application. You can now look at the traceback which led to the error. To switch between the interactive traceback and the plaintext one, you can click on the "Traceback" headline. From the text traceback you can also create a paste of it. For code execution mouse-over the frame you want to debug and click on the console icon on the right side. You can execute arbitrary Python code in the stack frames and there are some extra helpers available for introspection: dump() shows all variables in the frame dump(obj) dumps all that's known about the object

vscodeで作業をしています。
Python 3.10.7 64bit  です。

駆け出しの身でflaskのdeleteの書き方がわからずつまづいております。htmlもぐちゃぐちゃですがアドバイスいただきたいです。

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

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

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

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

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

guest

回答1

0

ベストアンサー

エラーの原因

エラーの最後から 6~8行目(空白行除く)を読むと

File "/Users/kojitakemura/Desktop/pythonProject1 2元本 2/flaskr/main.py", line 58, in delete book = con.execute('SELECT * FROM books WHERE id = ?', [id]).fetchone() sqlite3.OperationalError: no such column: id

と書いてあります。

このエラーメッセージから、main.py 58 行目の

book = con.execute('SELECT * FROM books WHERE id = ?', [id]).fetchone()

と言う部分で「sqlite3.OperationalError: no such column: id 」というエラーが発生しているということが分かります。

このエラーは、データベースに「id」というカラムが存在していないために発生しています。
したがって、データベースに「id」というカラムが存在している必要があります。
以下、データベースに「id」カラムを作成し、id で書籍を管理するように作り変えます。


 

データベースに「id」カラムを作成し、id で書籍を管理するように作り変えるための修正

db.pyの修正

db.py では、database.db 内に books という名前のテーブルが存在するか判定し、存在しない場合は books テーブルを新規作成しています。
このとき、books テーブル内に id カラムも作成するように修正します。

db.py

1import sqlite3 2 3DATABASE = "database.db" 4 5def create_books_table(): 6 con = sqlite3.connect(DATABASE) 7 con.execute("CREATE TABLE IF NOT EXISTS books (id INTEGER PRIMARY KEY AUTOINCREMENT, title, price, arrival_day)") 8 con.close()

CERATE文のところで「id INTEGER PRIMARY KEY AUTOINCREMENT」を追加しています。
これは

  • 「id」と言う名前のカラムを作成
  • id の型(整数)、主キー制約(PRIMARY KEY)、自動付番(AUTOINCREMENT)を設定

の意味です。

2番目のように設定する理由はidの重複を防ぐためです。仮にidの重複を許してしまうと、削除する時に書籍を区別できず、指定した書籍と別の書籍を削除してしまう可能性があります。

index.html の修正

index.html を下記のように修正します。

  • データベースの id カラムを表示する列を追加(必須ではありませんが動作をわかりやすくするため)
  • 削除リンクに、コントローラ(main.py)から受け取った id をつける → このようにすることで、削除リンククリック時に書籍IDを delete.html に渡すことが可能になります。

index.html

1...略... 2 <table border="1"> 3 <tr> 4 <th>ID</th> 5 <th>入荷日</th> 6 <th>タイトル</th> 7 <th>金額</th> 8 <th>削除</th> 9 </tr> 10 11 {% for book in books %} 12 <tr> 13 <td>{{ book.id }}</td> 14 <td>{{ book.arrival_day }}</td> 15 <td>{{ book.title }}</td> 16 <td>{{ book.price }}円</td> 17 <td> 18 <a href="{{ url_for('delete', id=book.id) }}">Delete</a> 19 </td> 20 </tr> 21 {% endfor %} 22...略...

delete.html の修正

データベースの構造を変更したのに合わせて、delete.html も修正します。

delete.html

1...略... 2 3 <p>ID : {{ book[0] }}</p> 4 <p>Title: {{ book[1] }}</p> 5 <p>Price: {{ book[2] }}</p> 6 <p>Arrival Day: {{ book[3] }}</p> 7...略...

main.py の修正

root ('/') の修正

root ('/') では、index.html の books に、データベースから取得した id も渡すように修正します。

main.py

1 books.append({"title": row[0], "price": row[1], "arrival_day": row[2]}) 2        ↓ 3 books.append({"id": row[0], "title": row[1], "price": row[2], "arrival_day": row[3]})

/register の修正

id を 自動付番に変えた関係上、/register のSQL文はそのままだとエラーになります。
ここでは、データベースに追加するレコードのカラム名を明示的に指定するよう修正します。

main.py

1 con.execute('INSERT INTO books VALUES(?,?,?)',[title,price,arrival_day]) 2    ↓ 3 con.execute('INSERT INTO books (title, price, arrival_day) VALUES(?, ?, ?)',[title, price, arrival_day])

その他

form.html の後半、なぜかdelete.html の内容が重複しています。特別な目的がない限り、後半は削除した方が良いでしょう。


修正後全体

修正した各コード全体は以下のようになりす。

main.py

1from crypt import methods 2from turtle import title 3#db 追加 4from flaskr import app, db 5from flask import render_template, request, redirect, url_for 6 7from flaskr.db import DATABASE 8 9import sqlite3 10DATABASE = "database.db" 11 12 13@app.route('/') 14def index(): 15 con = sqlite3.connect(DATABASE) 16 db_books = con.execute("SELECT * FROM books").fetchall() 17 con.close 18 19 20 books = [] 21 for row in db_books: 22 books.append({"id": row[0], "title": row[1], "price": row[2], "arrival_day": row[3]}) 23 24 return render_template( 25 'index.html',books=books 26 ) 27 28@app.route("/form") 29def form(): 30 return render_template("form.html") 31 32@app.route("/register", methods=["POST"]) 33def register(): 34 title = request.form["title"] 35 price = request.form["price"] 36 arrival_day =request.form["arrival_day"] 37 38 con = sqlite3.connect(DATABASE) 39 con.execute('INSERT INTO books (title, price, arrival_day) VALUES(?, ?, ?)',[title, price, arrival_day]) 40 41 con.commit() 42 con.close() 43 return redirect(url_for("index")) 44 45@app.route('/delete/<int:id>', methods=['GET', 'POST']) 46def delete(id): 47 con = sqlite3.connect(DATABASE) 48 if request.method == 'POST': 49 con.execute('DELETE FROM books WHERE id = ?', [id]) 50 con.commit() 51 con.close() 52 return redirect(url_for('index')) 53 else: 54 book = con.execute('SELECT * FROM books WHERE id = ?', [id]).fetchone() 55 con.close() 56 if book: 57 return render_template('delete.html', book=book) 58 else: 59 return "Book not found", 404

db.py

1import sqlite3 2 3 4DATABASE = "database.db" 5 6def create_books_table(): 7 con = sqlite3.connect(DATABASE) 8 con.execute("CREATE TABLE IF NOT EXISTS books (id INTEGER PRIMARY KEY AUTOINCREMENT, title, price, arrival_day)") 9 con.close()

index.html

1<!DOCTYPE html> 2<html lang="ja"> 3 4<head> 5 <meta charset="UTF-8"> 6 <title>sapu-app</title> 7 8 <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> 9</head> 10 11<body> 12 13 <h1>書店</h1> 14 <h2>今月の新刊一覧</h2> 15 {% if books == [] %} 16 <p>今月の新刊情報はまだありません</p> 17 {% else %} 18 <table border="1"> 19 <tr> 20 <th>ID</th> 21 <th>入荷日</th> 22 <th>タイトル</th> 23 <th>金額</th> 24 <th>削除</th> 25 </tr> 26 27 {% for book in books %} 28 <tr> 29 <td>{{ book.id }}</td> 30 <td>{{ book.arrival_day }}</td> 31 <td>{{ book.title }}</td> 32 <td>{{ book.price }}円</td> 33 <td> 34 <a href="{{ url_for('delete', id=book.id) }}">Delete</a> 35 </td> 36 </tr> 37 {% endfor %} 38 </table> 39 {% endif %} 40 41 <a href= "{{ url_for('form') }}">編集</a> 42</body> 43</html>

delete.html

1<!DOCTYPE html> 2<html lang="jp"> 3<head> 4 <meta charset="UTF-8"> 5 <title>Delete Book</title> 6</head> 7<body> 8 <h1>Delete Book</h1> 9 <p>Are you sure you want to delete the following book?</p> 10 <p>ID : {{ book[0] }}</p> 11 <p>Title: {{ book[1] }}</p> 12 <p>Price: {{ book[2] }}</p> 13 <p>Arrival Day: {{ book[3] }}</p> 14 <form method="POST"> 15 <input type="submit" value="Delete"> 16 </form> 17</body> 18</html>

form.html

1<!DOCTYPE html> 2<html lang="ja"> 3 4<head> 5 <meta charset="UTF-8"> 6 <title>sapu-app</title> 7 8 <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> 9 10</head> 11 12<body> 13 <h1>書店</h1> 14 <form method="post" action="{{ url_for('register') }}"> 15 <table border="1"> 16 <tr> 17 <th>入荷日</th> 18 <th>タイトル</th> 19 <th>金額</th> 20 </tr> 21 <tr> 22 <td><input type="text" name="arrival_day"></td> 23 <td><input type="text" name="title"></td> 24 <td><input type="text" name="price"></td> 25 </tr> 26 </table> 27 <br> 28 <input type="submit" value="登録"> 29 </form> 30</body> 31</html>

注意

上記修正内容を試す場合は、修正後初回のみ、一旦 flask を終了し、プロジェクト内の database.db ファイルを削除してから run.py を再実行するようにしてください。
修正前のdatabase.dbが残っている状態で起動した場合、うまく動作しません。


追記

あまり最初から詰め込みすぎると混乱の原因となるため、ここでは詳細は書きませんが、上記のコードではエラーではないものの、まだ以下の問題点があります。

  • main.pyの@app.route('/')で、booksリストにインデックスを使ってデータを渡している→この結果、何か修正の必要が生じた場合に、データベースに格納されているデータのカラムの順番を意識しないといけない

(たとえば上記だと、idというカラムを先頭に追加したため、delete.html の表示インデックスを1つずらしている)

これについては、データベースからデータを取得する際に row_factory を使用することで少しメンテしやすくなります。興味があれば調べてみるのもよいでしょう。

投稿2023/09/30 23:44

編集2023/10/01 02:54
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

takeko41

2023/10/02 16:55

ご回答いただき誠にありがとうございます。以上の変更をいたしましたところ、無事正常に作動いたしました。 各コードに対し添削だけでなく、学習のアドバイスまで頂き、駆け出しの身としてこの上無い僥倖です。 row_factoryやデータベース関連についてもう少し深掘りし学習してみようかと思います、改めましてご回答いただき誠にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問