FlaskアプリケーションでSQLiteデータベースを使用する方法
著者は、 Write for DOnations プログラムの一環として、 Free and Open SourceFundを選択して寄付を受け取りました。
序章
Webアプリケーションでは、通常、データの整理されたコレクションであるデータベースが必要です。 データベースを使用して、効率的に取得および操作できる永続データを保存および維持します。 たとえば、ソーシャルメディアアプリケーションでは、ユーザーデータ(個人情報、投稿、コメント、フォロワー)が効率的に操作できる方法で保存されているデータベースがあります。 さまざまな要件や条件に応じて、データベースにデータを追加、取得、変更、または削除できます。 Webアプリケーションでは、これらの要件は、ユーザーが新しい投稿を追加したり、投稿を削除したり、アカウントを削除したりする場合があります。これにより、投稿が削除される場合と削除されない場合があります。 データを操作するために実行するアクションは、アプリケーションの特定の機能によって異なります。 たとえば、タイトルのない投稿をユーザーに追加させたくない場合があります。
Flask は、Python言語でWebアプリケーションを作成するための便利なツールと機能を提供する軽量のPythonWebフレームワークです。 SQLite は、シンプルで高速なオープンソースの SQLエンジンであり、Pythonで使用してアプリケーションデータを保存および操作できます。 SQLiteはPythonでうまく機能します。これは、Python標準ライブラリが sqlite3モジュールを提供し、何もインストールせずにSQLiteデータベースと対話するために使用できるためです。 PythonでSQLiteを使用すると、他のデータベースエンジンと比較して最小限のセットアップも必要になります。
このチュートリアルでは、FlaskでSQLiteを使用して、CRUD(作成、読み取り、更新、削除)をカバーする基本的なデータ操作を実行する方法を示す小さなWebアプリケーションを構築します。 Webアプリケーションは、インデックスページに投稿を表示する基本的なブログになります。 ユーザーは、個々の投稿を作成、編集、および削除できます。
前提条件
- ローカルのPython3プログラミング環境については、 Python3シリーズのローカルプログラミング環境をインストールしてセットアップする方法のチュートリアルに従ってください。 このチュートリアルでは、プロジェクトディレクトリを
flask_appと呼びます。 - ルート、ビュー関数、テンプレートなどの基本的なFlaskの概念の理解。 Flaskに慣れていない場合は、FlaskとPythonを使用して最初のWebアプリケーションを作成する方法およびFlaskアプリケーションでテンプレートを使用する方法を確認してください。
- 基本的なHTMLの概念の理解。 背景知識については、HTMLを使用してWebサイトを構築する方法チュートリアルシリーズを確認できます。
- SQLiteの使用方法の基本的な理解。 Ubuntu20.04にSQLiteをインストールして使用する方法を参照してください。
ステップ1—データベースのセットアップ
このステップでは、データ(アプリケーションのブログ投稿)を保存するために使用するSQLiteデータベースを設定します。 次に、データベースにいくつかのエントリ例を入力します。
sqlite3 モジュールを使用して、データベースと対話します。データベースは、標準のPythonライブラリですぐに利用できます。
SQLiteのデータはテーブルと列に保存されるため、最初に必要な列を含むpostsというテーブルを作成する必要があります。 SQLコマンドを含む.sqlファイルを作成して、いくつかの列を持つpostsテーブルを作成します。 次に、このスキーマファイルを使用してデータベースを作成します。
flask_appディレクトリ内にあるschema.sqlというデータベーススキーマファイルを開きます。
nano schema.sql
このファイル内に次のSQLコマンドを入力します。
フラスコ_app/schema.sql
DROP TABLE IF EXISTS posts;
CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
title TEXT NOT NULL,
content TEXT NOT NULL
);
ファイルを保存して閉じます。
このスキーマファイルでは、最初にpostsテーブルがすでに存在する場合は削除します。 これにより、postsという名前の別のテーブルが存在する可能性が回避され、混乱を招く動作が発生する可能性があります(たとえば、列が異なる場合)。 テーブルをまだ作成していないため、SQLコマンドは実行されないため、ここでは当てはまりません。 これにより、このスキーマファイルを実行するたびに既存のデータがすべて削除されることに注意してください。 この目的のために、このスキーマは1回だけ実行しますが、挿入したデータを削除して空のデータベースから再開するために、もう一度実行することをお勧めします。
次に、CREATE TABLE postsを使用して、次の列を持つpostsテーブルを作成します。
id:主キーを表す整数。 このキーには、各エントリ(つまり、各ブログ投稿)のデータベースによって一意の値が割り当てられます。AUTOINCREMENTは投稿IDを自動的にインクリメントするため、最初の投稿のIDは1になり、その後に追加された投稿のIDは2になります。 。 他の投稿が削除された場合でも、各投稿は常に同じIDを持ちます。created:ブログ投稿が作成された時刻。NOT NULLは、この列が空であってはならないことを意味し、DEFAULT値は、投稿がデータベースに追加された時刻であるCURRENT_TIMESTAMP値です。idと同様に、この列の値は自動的に入力されるため、指定する必要はありません。title:投稿のタイトル。NOT NULLは、この列を空にすることはできないことを示します。content:投稿の内容。NOT NULLは、この列を空にすることはできないことを示します。
次に、schema.sqlファイルを使用してデータベースを作成します。 そのためには、このschema.sqlファイルに基づいてSQLite.dbデータベースファイルを生成するPythonファイルを作成します。 flask_appディレクトリ内にinit_db.pyという名前のファイルを開きます。
nano init_db.py
次のコードを追加します。
フラスコ_app/init_db.py
import sqlite3
connection = sqlite3.connect('database.db')
with open('schema.sql') as f:
connection.executescript(f.read())
cur = connection.cursor()
cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
('First Post', 'Content for the first post')
)
cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
('Second Post', 'Content for the second post')
)
connection.commit()
connection.close()
最初にsqlite3モジュールをインポートします。 database.dbという名前のデータベースファイルへの接続を開きます。このファイルは、Pythonファイルを実行すると作成されます。 次に、open()関数を使用して、schema.sqlファイルを開きます。 次に、複数のSQLステートメントを一度に実行する executescript()メソッドを使用してその内容を実行します。これにより、postsテーブルが作成されます。 データベース内の行を処理できるようにするカーソルオブジェクトを作成します。 この場合、カーソルの execute()メソッドを使用して2つのINSERT SQLステートメントを実行し、2つのブログ投稿をpostsテーブルに追加します。 最後に、変更をコミットして接続を閉じます。
ファイルを保存して閉じ、pythonコマンドを使用してターミナルで実行します。
python init_db.py
ファイルの実行が完了すると、database.dbという新しいファイルがflask_appディレクトリに表示されます。 これは、データベースが正常にセットアップされたことを意味します。
次に、小さなFlaskアプリケーションを作成し、データベースに挿入した2つの投稿を取得して、インデックスページに表示します。
ステップ2—投稿を表示する
このステップでは、データベースにあるブログ投稿が表示されるインデックスページを使用してFlaskアプリケーションを作成します。
プログラミング環境をアクティブにしてFlaskをインストールしたら、flask_appディレクトリ内で編集するためにapp.pyというファイルを開きます。
nano app.py
このファイルはデータベース接続をセットアップし、その接続を使用する単一のFlaskルートを作成します。 次のコードをファイルに追加します。
フラスコ_app/app.py
import sqlite3
from flask import Flask, render_template
app = Flask(__name__)
def get_db_connection():
conn = sqlite3.connect('database.db')
conn.row_factory = sqlite3.Row
return conn
@app.route('/')
def index():
conn = get_db_connection()
posts = conn.execute('SELECT * FROM posts').fetchall()
conn.close()
return render_template('index.html', posts=posts)
ファイルを保存して閉じます。
上記のコードでは、最初にsqlite3モジュールをインポートして、データベースへの接続に使用します。 次に、Flaskクラスとrender_template()関数をflaskパッケージからインポートします。 appというFlaskアプリケーションインスタンスを作成します。 get_db_connection()という関数を定義します。この関数は、前に作成したdatabase.dbデータベースファイルへの接続を開き、row_factory属性をsqlite3.Rowに設定します。列への名前ベースのアクセスを持つことができます。 これは、データベース接続が通常のPythonディクショナリのように動作する行を返すことを意味します。 最後に、この関数は、データベースへのアクセスに使用するconn接続オブジェクトを返します。
次に、app.route()デコレータを使用して、index()と呼ばれるFlaskビュー関数を作成します。 get_db_connection()関数を使用して、データベース接続を開きます。 次に、SQLクエリを実行して、postsテーブルからすべてのエントリを選択します。 fetchall()メソッドを使用して、クエリ結果のすべての行をフェッチします。これにより、前の手順でデータベースに挿入した投稿のリストが返されます。
close()メソッドを使用してデータベース接続を閉じ、index.htmlテンプレートをレンダリングした結果を返します。 また、postsオブジェクトを引数として渡します。このオブジェクトには、データベースから取得した結果が含まれています。 これにより、index.htmlテンプレートのブログ投稿にアクセスできるようになります。
データベースにある投稿をインデックスページに表示するには、最初にベーステンプレートを作成します。このテンプレートには、コードの繰り返しを避けるために他のテンプレートでも使用されるすべての基本的なHTMLコードが含まれます。 次に、index()関数でレンダリングしたindex.htmlテンプレートファイルを作成します。 テンプレートの詳細については、Flaskアプリケーションでテンプレートを使用する方法を参照してください。
テンプレートディレクトリを作成し、base.htmlという名前の新しいテンプレートを開きます。
mkdir templates nano templates/base.html
base.htmlファイル内に次のコードを追加します。
フラスコ_app/templates / base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %} {% endblock %}- FlaskApp</title>
<style>
.post {
padding: 10px;
margin: 5px;
background-color: #f3f3f3
}
nav a {
color: #d64161;
font-size: 3em;
margin-left: 50px;
text-decoration: none;
}
</style>
</head>
<body>
<nav>
<a href="{{ url_for('index') }}">FlaskApp</a>
<a href="#">About</a>
</nav>
<hr>
<div class="content">
{% block content %} {% endblock %}
</div>
</body>
</html>
ファイルを保存して閉じます。
この基本テンプレートには、他のテンプレートで再利用する必要があるすべてのHTMLボイラープレートが含まれています。 titleブロックは各ページのタイトルを設定するために置き換えられ、contentブロックは各ページのコンテンツに置き換えられます。 ナビゲーションバーには2つのリンクがあります。1つはurl_for()ヘルパー機能を使用してindex()ビュー機能にリンクするインデックスページ用で、もう1つは[バージョン情報]ページ用です。アプリケーションで。
次に、index.htmlというテンプレートを開きます。 これは、app.pyファイルで参照したテンプレートです。
nano templates/index.html
次のコードを追加します。
フラスコ_app/templates / index.html
{% extends 'base.html' %}
{% block content %}
<h1>{% block title %} Posts {% endblock %}</h1>
{% for post in posts %}
<div class='post'>
<p>{{ post['created'] }}</p>
<h2>{{ post['title'] }}</h2>
<p>{{ post['content'] }}</p>
</div>
{% endfor %}
{% endblock %}
ファイルを保存して閉じます。
上記のコードでは、base.htmlテンプレートを拡張し、contentブロックの内容を置き換えます。 タイトルを兼ねる<h1>の見出しを使用します。
{% for post in posts %}行のJinjafor loop を使用して、postsリストの各投稿を調べます。 {{ post['created'] }}から作成日、{{ post['title'] }}からタイトル、{{ post['content'] }}から投稿コンテンツにアクセスします。
仮想環境がアクティブになっているflask_appディレクトリで、FLASK_APP環境変数を使用してアプリケーション(この場合はapp.py)についてFlaskに通知します。
export FLASK_APP=app
次に、FLASK_ENV環境変数をdevelopmentに設定して、アプリケーションを開発モードで実行し、デバッガーにアクセスします。 Flaskデバッガーの詳細については、Flaskアプリケーションでエラーを処理する方法を参照してください。 これを行うには、次のコマンドを使用します(Windowsでは、exportの代わりにsetを使用します)。
export FLASK_ENV=development
次に、アプリケーションを実行します。
flask run
開発サーバーが実行されている状態で、ブラウザーを使用して次のURLにアクセスします。
http://127.0.0.1:5000/
最初の開始時にデータベースに追加した投稿が表示されます。
データベースの投稿をインデックスページに表示しました。 ここで、ユーザーが新しい投稿を追加できるようにする必要があります。 次のステップで、投稿を追加するための新しいルートを追加します。
ステップ3—投稿を作成する
このステップでは、Flaskアプリケーションに新しいルートを追加します。これにより、ユーザーは新しいブログ投稿をデータベースに追加でき、インデックスページに表示されます。
ユーザーが投稿のタイトルと投稿のコンテンツを入力するWebフォームを含むページを追加します。 このフォームは、ユーザーが空のフォームを送信しないように検証されます。 フォームが無効であることをユーザーに通知するには、フラッシュメッセージを使用します。このメッセージは1回だけ表示され、次のリクエストで消えます(たとえば、別のページに移動した場合)。
開発サーバーを実行したままにして、新しいターミナルウィンドウを開きます。
まず、app.pyファイルを開きます。
nano app.py
Webフォームを処理するには、flaskパッケージからいくつかのものをインポートする必要があります。
- 送信されたデータにアクセスするためのグローバルrequestオブジェクト。
- url_for()関数を使用してURLを生成します。
- flash()関数は、リクエストが無効な場合にメッセージをフラッシュします。
- redirect()関数は、データベースに投稿を追加した後、ユーザーをインデックスページにリダイレクトします。
これらのインポートをファイルの最初の行に追加します。
フラスコ_app/app.py
from flask import Flask, render_template, request, url_for, flash, redirect # ...
flash()関数は、フラッシュされたメッセージをクライアントのブラウザセッションに保存します。これには、ある要求から別の要求への情報を記憶するセッションを保護するために秘密鍵を設定する必要があります。 誰にもあなたの秘密鍵へのアクセスを許可してはなりません。 詳細については、セッションのFlaskドキュメントを参照してください。
app.configオブジェクトを介してアプリケーションにSECRET_KEY構成を追加することにより、秘密鍵を設定します。 appインスタンス定義の横に追加します。
フラスコ_app/app.py
# ... app = Flask(__name__) app.config['SECRET_KEY'] = 'your secret key'
秘密鍵は長いランダムな文字列である必要があることに注意してください。 Webフォームと秘密鍵の構成の詳細については、FlaskアプリケーションでWebフォームを使用する方法を参照してください。
次に、app.pyファイルの最後に次のルートを追加します。
フラスコ_app/app.py
# ...
@app.route('/create/', methods=('GET', 'POST'))
def create():
return render_template('create.html')
ファイルを保存して閉じます。
このルートでは、タプル('GET', 'POST')をmethodsパラメーターに渡して、GET要求とPOST要求の両方を許可します。 GETリクエストは、サーバーからデータを取得するために使用されます。 POSTリクエストは、特定のルートにデータを投稿するために使用されます。 デフォルトでは、GETリクエストのみが許可されます。 ユーザーが最初にGETリクエストを使用して/createルートをリクエストすると、create.htmlというテンプレートファイルがレンダリングされます。 後でこのルートを編集して、ユーザーが新しい投稿を作成するためのWebフォームに入力して送信するときのPOSTリクエストを処理します。
新しいcreate.htmlテンプレートを開きます。
nano templates/create.html
次のコードを追加します。
フラスコ_app/templates / create.html
{% extends 'base.html' %}
{% block content %}
<h1>{% block title %} Add a New Post {% endblock %}</h1>
<form method="post">
<label for="title">Title</label>
<br>
<input type="text" name="title"
placeholder="Post title"
value="{{ request.form['title'] }}"></input>
<br>
<label for="content">Post Content</label>
<br>
<textarea name="content"
placeholder="Post content"
rows="15"
cols="60"
>{{ request.form['content'] }}</textarea>
<br>
<button type="submit">Submit</button>
</form>
{% endblock %}
ファイルを保存して閉じます。
基本テンプレートを拡張し、見出しをタイトルとして設定し、属性methodをpostに設定した<form>タグを使用して、フォームがPOSTリクエストを送信することを示します。 titleという名前のテキストフィールドがあり、これを使用して/createルートのタイトルデータにアクセスします。 テキストフィールドの値をrequest.form['title']に設定します。これは空であるか、フォームが無効な場合はタイトルの保存バージョンであり、問題が発生したときにタイトルが失われないようにします。
タイトル入力フィールドの後に、contentという名前のテキスト領域に値{{ request.form['content'] }}を追加して、フォームが無効な場合に投稿コンテンツを復元します。
最後に、フォームの最後に[送信]ボタンがあります。
これで、開発サーバーが実行されている状態で、ブラウザーを使用して/createルートに移動します。
http://127.0.0.1:5000/create
新しい投稿の追加ページが表示され、投稿タイトルの入力フィールド、投稿のコンテンツのテキスト領域、および送信ボタンが表示されます。
フォームに入力して送信し、サーバーにPOSTリクエストを送信しても、/createルートでPOSTリクエストを処理しなかったため、何も起こりません。
app.pyを開いて、ユーザーが送信するPOST要求を処理します。
nano app.py
/createルートを編集して、次のようにします。
フラスコ_app/app.py
# ...
@app.route('/create/', methods=('GET', 'POST'))
def create():
if request.method == 'POST':
title = request.form['title']
content = request.form['content']
if not title:
flash('Title is required!')
elif not content:
flash('Content is required!')
else:
conn = get_db_connection()
conn.execute('INSERT INTO posts (title, content) VALUES (?, ?)',
(title, content))
conn.commit()
conn.close()
return redirect(url_for('index'))
return render_template('create.html')
ファイルを保存して閉じます。
if request.method == 'POST'条件内でPOST要求を処理します。 request.formオブジェクトからユーザーが送信するタイトルとコンテンツを抽出します。 タイトルが空の場合は、flash()機能を使用してメッセージTitle is required!を点滅させます。 空のコンテンツの場合も同じようにします。
タイトルとコンテンツの両方が指定されている場合は、get_db_connection()関数を使用してデータベース接続を開きます。 execute()メソッドを使用してINSERT INTOSQLステートメントを実行し、ユーザーが値として送信するタイトルとコンテンツを含む新しい投稿をpostsテーブルに追加します。 ?プレースホルダーを使用して、データをテーブルに安全に挿入します。 トランザクションをコミットして、接続を閉じます。 最後に、ユーザーをインデックスページにリダイレクトします。このページでは、既存の投稿の下に新しい投稿が表示されます。
警告:Python文字列操作を使用してSQLステートメント文字列を動的に作成しないでください。 値を動的に置き換えるには、SQLステートメントで常に?プレースホルダーを使用してください。 値のタプルを2番目の引数としてexecute()メソッドに渡し、値をSQLステートメントにバインドします。 これにより、SQLインジェクション攻撃が防止されます。
開発サーバーが実行されている状態で、ブラウザーを使用して/createルートに移動します。
http://127.0.0.1:5000/create
フォームに記入して送信します。
新しい投稿が表示されるインデックスページにリダイレクトされます。
タイトルのないフォームまたはコンテンツのないフォームを送信すると、投稿はデータベースに追加されず、インデックスページにリダイレクトされず、その理由に関するフィードバックを受け取ることもありません。 これは、フラッシュされたメッセージがまだどこにも表示されないように設定していないためです。
base.htmlを開いて、ナビゲーションバーの[作成]ページへのリンクを追加し、その下に点滅するメッセージを表示します。
nano templates/base.html
次のようにファイルを編集します。
フラスコ_app/templates / base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %} {% endblock %} - FlaskApp</title>
<style>
.post {
padding: 10px;
margin: 5px;
background-color: #f3f3f3
}
nav a {
color: #d64161;
font-size: 3em;
margin-left: 50px;
text-decoration: none;
}
.alert {
padding: 20px;
margin: 5px;
color: #970020;
background-color: #ffd5de;
}
</style>
</head>
<body>
<nav>
<a href="{{ url_for('index') }}">FlaskApp</a>
<a href="{{ url_for('create') }}">Create</a>
<a href="#">About</a>
</nav>
<hr>
<div class="content">
{% for message in get_flashed_messages() %}
<div class="alert">{{ message }}</div>
{% endfor %}
{% block content %} {% endblock %}
</div>
</body>
</html>
ファイルを保存して閉じます。
ここでは、作成ページを指すナビゲーションバーに新しい<a>リンクを追加します。
Jinja forループを使用して、フラッシュされたメッセージを確認します。 これらは、get_flashed_messages()特殊機能で使用できます。 各メッセージは、alertというCSSクラスの<div>タグで表示されます。 この<div>タグは、<head>セクションの<style>タグ内でスタイルを設定します。
インデックスページを更新すると、ナビゲーションバーに新しいリンクが表示されます。
作成リンクをクリックして、空のフォームを送信します。 「タイトルが必要です!」というメッセージが点滅します。
タイトルフィールドに入力し、コンテンツテキスト領域を空のままにします。 もう一度フォームを送信すると、「コンテンツが必要です!」というメッセージが表示されます。 メッセージ。 「タイトルが必要です!」に注目してください。 メッセージが消えました。 これは、フラッシュメッセージであり、永続的なメッセージではないためです。
これで、新しい投稿を追加する方法があります。 次に、ユーザーが既存の投稿を編集できるようにするための新しいルートを追加します。
ステップ4—投稿を編集する
このステップでは、ユーザーが既存の投稿を編集できるように、アプリケーションに新しいルートを追加します。
まず、コードの繰り返しを回避し、コードを分離して保守を容易にするために、IDを取得し、それに関連付けられた投稿をデータベースから取得する新しい関数を追加します。 この関数を使用して、編集する投稿データを取得し、次のステップで削除する場合の投稿を取得します。
app.pyを開きます:
nano app.py
投稿を取得するために使用する関数は、要求された投稿のIDが既存の投稿のいずれとも一致しない場合、404 Not Foundエラーで応答します。 これを行うには、 abort()関数を使用します。この関数は、要求を中止し、エラーメッセージで応答します。 詳細については、Flaskアプリケーションでエラーを処理する方法を参照してください。
abort()関数をインポートに追加します。
フラスコ_app/app.py
from flask import Flask, render_template, request, url_for, flash, redirect, abort
get_db_connection()関数の下にget_post()という新しい関数を追加します。
フラスコ_app/app.py
# ...
def get_db_connection():
conn = sqlite3.connect('database.db')
conn.row_factory = sqlite3.Row
return conn
def get_post(post_id):
conn = get_db_connection()
post = conn.execute('SELECT * FROM posts WHERE id = ?',
(post_id,)).fetchone()
conn.close()
if post is None:
abort(404)
return post
# ...
この新しい関数には、取得して返す投稿を決定するpost_id引数があります。 get_db_connection()とのデータベース接続を開き、SQLクエリを実行して、指定されたpost_id値に関連付けられた投稿を取得します。 fetchone()メソッドを使用して投稿を取得し、それをpost変数に格納して、接続を閉じます。
post変数の値がNoneの場合、つまりデータベースに結果が見つからなかった場合は、前にインポートしたabort()関数を使用して、[X170Xで応答します。 ]エラーコードと関数は実行を終了します。 ただし、投稿が見つかった場合は、post変数の値を返します。
次に、ファイルの最後に投稿を編集するための新しいルートを追加します。
フラスコ_app/app.py
# ...
@app.route('/<int:id>/edit/', methods=('GET', 'POST'))
def edit(id):
post = get_post(id)
if request.method == 'POST':
title = request.form['title']
content = request.form['content']
if not title:
flash('Title is required!')
elif not content:
flash('Content is required!')
else:
conn = get_db_connection()
conn.execute('UPDATE posts SET title = ?, content = ?'
' WHERE id = ?',
(title, content, id))
conn.commit()
conn.close()
return redirect(url_for('index'))
return render_template('edit.html', post=post)
ファイルを保存して閉じます。
ルート/<int:id>/edit/を使用し、int:は正の整数を受け入れるコンバーターです。 また、idは、編集する投稿を決定するURL変数です。 たとえば、/2/edit/を使用すると、IDが2の投稿を編集できます。 IDはURLからedit()ビュー関数に渡されます。 id引数の値をget_post()関数に渡して、提供されたIDに関連付けられた投稿をデータベースからフェッチします。 指定されたIDの投稿が存在しない場合、これは404 Not Foundエラーで応答することに注意してください。
最後の行は、edit.htmlというテンプレートファイルをレンダリングし、投稿データを含むpost変数を渡します。 これを使用して、編集ページに既存のタイトルとコンテンツを表示します。
if request.method == 'POST'ブロックは、ユーザーが送信する新しいデータを処理します。 新しい投稿を追加するのと同様に、タイトルとコンテンツを抽出します。 タイトルまたはコンテンツが提供されていない場合は、メッセージをフラッシュします。
フォームが有効な場合は、データベース接続を開き、UPDATE SQLステートメントを使用して、新しいタイトルと新しいコンテンツを設定することにより、postsテーブルを更新します。ここで、データベース内の投稿のID URLに含まれていたIDと同じです。 トランザクションをコミットし、接続を閉じて、インデックスページにリダイレクトします。
次に、ユーザーが編集できるページを作成する必要があります。 新しいedit.htmlテンプレートを開きます。
nano templates/edit.html
次のコードを追加します。
フラスコ_app/templates / edit.html
{% extends 'base.html' %}
{% block content %}
<h1>{% block title %} Edit "{{ post['title'] }}" {% endblock %}</h1>
<form method="post">
<label for="title">Title</label>
<br>
<input type="text" name="title"
placeholder="Post title"
value="{{ request.form['title'] or post['title'] }}"></input>
<br>
<label for="content">Post Content</label>
<br>
<textarea name="content"
placeholder="Post content"
rows="15"
cols="60"
>{{ request.form['content'] or post['content'] }}</textarea>
<br>
<button type="submit">Submit</button>
</form>
{% endblock %}
ファイルを保存して閉じます。
これは、create.htmlテンプレートのコードと似ていますが、ページのタイトル内の{% block title %} Edit "{{ post['title'] }}" {% endblock %}行の投稿タイトル、{{ request.form['title'] or post['title'] }}の入力値、および{{ request.form['content'] or post['content'] }}のテキスト領域の値。 これにより、リクエストに保存されているデータが存在する場合はそれが表示されます。 それ以外の場合は、現在のデータベースデータを含むテンプレートに渡されたpost変数からのデータが表示されます。
開発サーバーが実行されている状態で、ブラウザーを使用して次のURLに移動し、最初の投稿を編集します。
http://127.0.0.1:5000/1/edit
次のようなページが表示されます。
投稿を編集してフォームを送信します。 インデックスページに適用された変更が表示されます。 タイトルなしまたはコンテンツなしでフォームを送信すると、フラッシュメッセージが表示されます。
次に、インデックスページの各投稿の編集ページを指すリンクを追加する必要があります。 index.htmlテンプレートファイルを開きます。
nano templates/index.html
次のようにファイルを編集します。
フラスコ_app/templates / index.html
{% extends 'base.html' %}
{% block content %}
<h1>{% block title %} Posts {% endblock %}</h1>
{% for post in posts %}
<div class='post'>
<p>{{ post['created'] }}</p>
<h2>{{ post['title'] }}</h2>
<p>{{ post['content'] }}</p>
<a href="{{ url_for('edit', id=post['id']) }}">Edit</a>
</div>
{% endfor %}
{% endblock %}
ファイルを保存して閉じます。
edit()ビュー機能にリンクする<a>タグを追加しました。 post['id'])にある投稿IDをurl_for()関数に渡して、投稿の編集リンクを生成します。 これにより、その下にある各投稿の編集ページへのリンクが追加されます。
インデックスページを更新し、編集リンクをクリックして投稿を編集します。
これで、新しい投稿を追加したり、既存の投稿を編集したりできます。 次に、ユーザーが既存の投稿を削除できるようにするボタンを追加します。
ステップ5—投稿を削除する
このステップでは、ユーザーが投稿を削除できるように、[編集]ページに[削除]ボタンを追加します。
まず、edit()ビュー機能と同様に、POSTリクエストを受け入れる新しい/id/deleteルートを追加します。 新しいdelete()ビュー関数は、URLから削除する投稿のIDを受け取り、get_post()関数を使用してそれを取得し、存在する場合はデータベースから削除します。
app.pyファイルを開きます。
nano app.py
最後に次のルートを追加します。
フラスコ_app/app.py
# ...
@app.route('/<int:id>/delete/', methods=('POST',))
def delete(id):
post = get_post(id)
conn = get_db_connection()
conn.execute('DELETE FROM posts WHERE id = ?', (id,))
conn.commit()
conn.close()
flash('"{}" was successfully deleted!'.format(post['title']))
return redirect(url_for('index'))
ファイルを保存して閉じます。
このビュー関数は、methodsパラメーターのPOST要求のみを受け入れます。 これは、ブラウザで/ID/deleteルートに移動すると、405 Method Not Allowedエラーが返されることを意味します。これは、WebブラウザがデフォルトでGETリクエストを使用しているためです。 投稿を削除するには、ユーザーはこのルートにPOSTリクエストを送信するボタンをクリックします。
この関数は、削除する投稿のIDを受け取ります。 このIDを使用して、get_post()関数を使用して投稿を取得します。 指定されたIDの投稿が存在しない場合、これは404 Not Foundエラーで応答します。 データベース接続を開き、DELETE FROMSQLコマンドを実行して投稿を削除します。 WHERE id = ?を使用して、削除する投稿を指定します。
データベースへの変更をコミットし、接続を閉じます。 メッセージをフラッシュして、投稿が正常に削除されたことをユーザーに通知し、インデックスページにリダイレクトします。
テンプレートファイルはレンダリングしないことに注意してください。 これは、[編集]ページに[削除]ボタンを追加するだけだからです。
edit.htmlテンプレートファイルを開きます。
nano templates/edit.html
次に、{% endblock %}行の直前に次の<hr>および<form>タグを追加します。
フラスコ_app/templates / edit.html
<button type="submit">Submit</button>
</form>
<hr>
<form action="{{ url_for('delete', id=post['id']) }}" method="POST">
<input type="submit" value="Delete Post"
onclick="return confirm('Are you sure you want to delete this post?')">
</form>
{% endblock %}
ファイルを保存して閉じます。
ここに、POSTリクエストをdelete()ビュー関数に送信するWebフォームがあります。 post['id']を渡して、削除する投稿を指定します。 Webブラウザーで使用可能なconfirm()メソッドを使用して、要求を送信する前に確認メッセージを表示します。
次に、投稿の[編集]ページに再度移動して、削除してみます。
http://127.0.0.1:5000/1/edit
削除を確認すると、インデックスページにリダイレクトされ、投稿は表示されなくなります。 ナビゲーションバーの下に、投稿が正常に削除されたことを通知するフラッシュメッセージが表示されます。
これで、Flaskアプリケーションのデータベースから不要な投稿を削除する方法があります。
結論
SQLiteデータベースと通信する小さなWebブログを作成しました。 Flaskアプリケーションには、データベースへの新しいデータの追加、データの取得とページへの表示、既存のデータの編集と削除などの基本的な機能があります。
PythonとFlaskでSQLiteを使用する方法の詳細については、次のチュートリアルを参照してください。
- Python3でsqlite3モジュールを使用する方法
- FlaskおよびSQLiteで1対多のデータベース関係を使用する方法
- FlaskおよびSQLiteとの1対多のデータベース関係でアイテムを変更する方法
- FlaskおよびSQLiteで多対多のデータベース関係を使用する方法
Flaskの詳細については、Flaskシリーズの他のチュートリアルをご覧ください。