著者は、 Write for DOnations プログラムの一環として、 Free and Open SourceFundを選択して寄付を受け取りました。

序章

Webアプリケーションでは、通常、データの組織化されたコレクションであるデータベースが必要です。 データベースを使用して、効率的に取得および操作できる永続データを保存および維持します。 たとえば、ソーシャルメディアアプリケーションでは、ユーザーデータ(個人情報、投稿、コメント、フォロワー)が効率的に操作できる方法で保存されているデータベースがあります。 さまざまな要件や条件に応じて、データベースにデータを追加、取得、変更、または削除できます。 Webアプリケーションでは、これらの要件は、ユーザーが新しい投稿を追加したり、投稿を削除したり、アカウントを削除したりする場合があります。これにより、投稿が削除される場合と削除されない場合があります。 データを操作するために実行するアクションは、アプリケーションの特定の機能によって異なります。 たとえば、ユーザーにタイトルのない投稿を追加させたくない場合があります。

Flaskは、Python言語でWebアプリケーションを作成するための便利なツールと機能を提供する軽量のPythonWebフレームワークです。 PostgreSQL 、またはPostgresは、SQLクエリ言語の実装を提供するリレーショナルデータベース管理システムです。 これは標準に準拠しており、信頼性の高いトランザクションや読み取りロックのない同時実行性など、多くの高度な機能を備えています。

このチュートリアルでは、PythonでPostgreSQLデータベースを操作できるPostgreSQLデータベースアダプタであるpsycopg2ライブラリの使用方法を示す小さな書評Webアプリケーションを作成します。 これをFlaskで使用して、データベースサーバーへの接続、テーブルの作成、テーブルへのデータの挿入、テーブルからのデータの取得などの基本的なタスクを実行します。

前提条件

ステップ1—PostgreSQLデータベースとユーザーの作成

このステップでは、Flaskアプリケーション用にflask_dbというデータベースとsammyというデータベースユーザーを作成します。

Postgresのインストール中に、postgresという名前のオペレーティングシステムユーザーが、postgresPostgreSQL管理ユーザーに対応するように作成されました。 管理タスクを実行するには、このユーザーを使用する必要があります。 sudoを使用して、-iuオプションを使用してユーザー名を渡すことができます。

次のコマンドを使用して、インタラクティブなPostgresセッションにログインします。

  1. sudo -iu postgres psql

要件を設定できるPostgreSQLプロンプトが表示されます。

まず、プロジェクトのデータベースを作成します。

  1. CREATE DATABASE flask_db;

注:すべてのPostgresステートメントはセミコロンで終了する必要があるため、問題が発生している場合は、コマンドがセミコロンで終了していることを確認してください。

次に、プロジェクトのデータベースユーザーを作成します。 安全なパスワードを選択してください。

  1. CREATE USER sammy WITH PASSWORD 'password';

次に、この新しいユーザーに新しいデータベースを管理するためのアクセス権を付与します。

  1. GRANT ALL PRIVILEGES ON DATABASE flask_db TO sammy;

データベースが作成されたことを確認するには、次のコマンドを入力してデータベースのリストを取得します。

  1. \l

データベースのリストにflask_dbが表示されます。

終了したら、次のように入力してPostgreSQLプロンプトを終了します。

  1. \q

これで、Postgresがセットアップされ、psycopg2ライブラリを使用してPython経由でデータベース情報に接続して管理できるようになりました。 次に、このライブラリをFlaskパッケージと一緒にインストールします。

ステップ2—Flaskとpsycopg2をインストールする

このステップでは、Flaskとpsycopg2ライブラリをインストールして、Pythonを使用してデータベースを操作できるようにします。

仮想環境をアクティブにした状態で、pipを使用してFlaskとpsycopg2ライブラリをインストールします。

  1. pip install Flask psycopg2-binary

インストールが正常に完了すると、出力の最後に次のような行が表示されます。

Output
Successfully installed Flask-2.0.2 Jinja2-3.0.3 MarkupSafe-2.0.1 Werkzeug-2.0.2 click-8.0.3 itsdangerous-2.0.1 psycopg2-binary-2.9.2

これで、必要なパッケージが仮想環境にインストールされました。 次に、データベースに接続してセットアップします。

ステップ3—データベースのセットアップ

このステップでは、flask_appプロジェクトディレクトリにPythonファイルを作成して、flask_dbデータベースに接続し、書籍を保存するためのテーブルを作成し、レビュー付きの書籍を挿入します。

まず、プログラミング環境をアクティブにして、flask_appディレクトリにあるinit_db.pyという新しいファイルを開きます。

  1. nano init_db.py

このファイルは、flask_dbデータベースへの接続を開き、booksというテーブルを作成し、サンプルデータを使用してテーブルにデータを入力します。 次のコードを追加します。

フラスコ_app/init_db.py
import os
import psycopg2

conn = psycopg2.connect(
        host="localhost",
        database="flask_db",
        user=os.environ['DB_USERNAME'],
        password=os.environ['DB_PASSWORD'])

# Open a cursor to perform database operations
cur = conn.cursor()

# Execute a command: this creates a new table
cur.execute('DROP TABLE IF EXISTS books;')
cur.execute('CREATE TABLE books (id serial PRIMARY KEY,'
                                 'title varchar (150) NOT NULL,'
                                 'author varchar (50) NOT NULL,'
                                 'pages_num integer NOT NULL,'
                                 'review text,'
                                 'date_added date DEFAULT CURRENT_TIMESTAMP);'
                                 )

# Insert data into the table

cur.execute('INSERT INTO books (title, author, pages_num, review)'
            'VALUES (%s, %s, %s, %s)',
            ('A Tale of Two Cities',
             'Charles Dickens',
             489,
             'A great classic!')
            )


cur.execute('INSERT INTO books (title, author, pages_num, review)'
            'VALUES (%s, %s, %s, %s)',
            ('Anna Karenina',
             'Leo Tolstoy',
             864,
             'Another great classic!')
            )

conn.commit()

cur.close()
conn.close()

ファイルを保存して閉じます。

このファイルでは、最初に os モジュールをインポートして、データベースのユーザー名とパスワードを保存する環境変数にアクセスし、ソースコードに表示されないようにします。

psycopg2ライブラリをインポートします。 次に、psycopg2.connect()関数を使用して、flask_dbデータベースへの接続を開きます。 ホストを指定します。この場合はローカルホストです。 データベース名をdatabaseパラメーターに渡します。

os.environオブジェクトを介してユーザー名とパスワードを指定します。これにより、プログラミング環境で設定した環境変数にアクセスできます。 データベースのユーザー名はDB_USERNAMEという環境変数に保存し、パスワードはDB_PASSWORDという環境変数に保存します。 これにより、ユーザー名とパスワードをソースコードの外部に保存できるため、ソースコードをソース管理に保存したり、インターネット上のサーバーにアップロードしたりするときに機密情報が漏洩することはありません。 攻撃者がソースコードにアクセスしたとしても、データベースにアクセスすることはできません。

connection.cursor()メソッドを使用して、curというカーソルを作成します。これにより、PythonコードでデータベースセッションでPostgreSQLコマンドを実行できます。

カーソルのexecute()メソッドを使用して、booksテーブルがすでに存在する場合は削除します。 これにより、booksという名前の別のテーブルが存在する可能性が回避され、混乱を招く動作が発生する可能性があります(たとえば、列が異なる場合)。 テーブルをまだ作成していないため、SQLコマンドは実行されないため、ここでは当てはまりません。 このinit_db.pyファイルを実行すると、既存のデータがすべて削除されることに注意してください。 この目的のために、データベースを開始するためにこのファイルを1回だけ実行しますが、挿入したデータを削除して最初のサンプルデータからやり直すために、もう一度実行することをお勧めします。

次に、CREATE TABLE booksを使用して、次の列を持つbooksという名前のテーブルを作成します。

  • id:自動インクリメント整数であるserialタイプのID。 この列は、PRIMARY KEYキーワードを使用して指定した主キーを表します。 データベースは、エントリごとにこのキーに一意の値を割り当てます。
  • titlevarcharタイプの本のタイトルで、制限付きの可変長の文字タイプです。 varchar (150)は、タイトルの長さが最大150文字であることを意味します。 NOT NULLは、この列を空にすることはできないことを示します。
  • author:本の著者。50文字以内です。 NOT NULLは、この列を空にすることはできないことを示します。
  • pages_num:本のページ数を表す整数。 NOT NULLは、この列を空にすることはできないことを示します。
  • review:書評。 textタイプは、レビューを任意の長さのテキストにすることができることを意味します。
  • date_added:本がテーブルに追加された日付。 DEFAULTは、列のデフォルト値をCURRENT_TIMESTAMPに設定します。これは、書籍がデータベースに追加された時刻です。 idと同様に、この列の値は自動的に入力されるため、指定する必要はありません。

テーブルを作成したら、カーソルのexecute()メソッドを使用して、2冊の本をテーブルに挿入します。チャールズディケンズの二都物語と、レオのアンナカレーニナです。トルストイ。 %sプレースホルダーを使用して、SQLステートメントに値を渡します。 psycopg2は、SQLインジェクション攻撃を防ぐ方法でバックグラウンドでの挿入を処理します。

書籍データをテーブルに挿入し終えたら、 connection.commit()メソッドを使用してトランザクションをコミットし、変更をデータベースに適用します。 次に、cur.close()でカーソルを閉じ、conn.close()で接続してクリーンアップします。

データベース接続を確立するには、次のコマンドを実行して、DB_USERNAMEおよびDB_PASSWORD環境変数を設定します。 自分のユーザー名とパスワードを使用することを忘れないでください。

  1. export DB_USERNAME="sammy"
  2. export DB_PASSWORD="password"

次に、pythonコマンドを使用して、ターミナルでinit_db.pyファイルを実行します。

  1. python init_db.py

ファイルがエラーなしで実行を終了すると、新しいbooksテーブルがflask_dbデータベースに追加されます。

インタラクティブなPostgresセッションにログインして、新しいbooksテーブルをチェックアウトします。

  1. sudo -iu postgres psql

\cコマンドを使用して、flask_dbデータベースに接続します。

  1. \c flask_db

次に、SELECTステートメントを使用して、booksテーブルから本のタイトルと著者を取得します。

  1. SELECT title, author FROM books;

次のような出力が表示されます。

        title         |      author
----------------------+------------------
 A Tale of Two Cities | Charles Dickens
 Anna Karenina        | Leo Tolstoy

\qでインタラクティブセッションを終了します。

次に、小さなFlaskアプリケーションを作成し、データベースに接続して、データベースに挿入した2つの書評を取得し、それらをインデックスページに表示します。

ステップ4—本を表示する

このステップでは、データベースにある本を取得して表示するインデックスページを使用してFlaskアプリケーションを作成します。

プログラミング環境をアクティブにしてFlaskをインストールしたら、flask_appディレクトリ内で編集するためにapp.pyというファイルを開きます。

  1. nano app.py

このファイルはデータベース接続をセットアップし、その接続を使用する単一のFlaskルートを作成します。 次のコードをファイルに追加します。

フラスコ_app/app.py
import os
import psycopg2
from flask import Flask, render_template

app = Flask(__name__)

def get_db_connection():
    conn = psycopg2.connect(host='localhost',
                            database='flask_db',
                            user=os.environ['DB_USERNAME'],
                            password=os.environ['DB_PASSWORD'])
    return conn


@app.route('/')
def index():
    conn = get_db_connection()
    cur = conn.cursor()
    cur.execute('SELECT * FROM books;')
    books = cur.fetchall()
    cur.close()
    conn.close()
    return render_template('index.html', books=books)

ファイルを保存して閉じます。

ここでは、osモジュール、psycopg2ライブラリ、Flaskクラス、およびrender_template()flaskパッケージからインポートします。 appというFlaskアプリケーションインスタンスを作成します。

get_db_connection()という関数を定義します。この関数は、DB_USERNAMEおよびDB_PASSWORD環境変数に保存したユーザーとパスワードを使用してflask_dbデータベースへの接続を開きます。 この関数は、データベースへのアクセスに使用するconn接続オブジェクトを返します。

次に、app.route()デコレータを使用して、メインの/ルートとindex()ビュー関数を作成します。 index()ビュー関数では、get_db_connection()関数を使用してデータベース接続を開き、カーソルを作成し、SELECT * FROM books; SQLステートメントを実行して、データベース。 fetchall()メソッドを使用して、booksという変数にデータを保存します。 次に、カーソルと接続を閉じます。 最後に、render_template()関数の呼び出しを返し、index.htmlというテンプレートファイルをレンダリングして、データベースからフェッチした書籍のリストをbooks変数に渡します。

データベースにある本をインデックスページに表示するには、最初にベーステンプレートを作成します。このテンプレートには、コードの繰り返しを避けるために他のテンプレートでも使用されるすべての基本的なHTMLコードが含まれます。 次に、index()関数でレンダリングしたindex.htmlテンプレートファイルを作成します。 テンプレートの詳細については、Flaskアプリケーションでテンプレートを使用する方法を参照してください。

templatesディレクトリを作成し、base.htmlという名前の新しいテンプレートを開きます。

  1. mkdir templates
  2. 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>
        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }

        .book {
            padding: 20px;
            margin: 10px;
            background-color: #f7f4f4;
        }

        .review {
                margin-left: 50px;
                font-size: 20px;
        }

    </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つはAboutページ用です。アプリケーションに含めることを選択した場合。

次に、index.htmlというテンプレートを開きます。 これは、app.pyファイルで参照したテンプレートです。

  1. nano templates/index.html

次のコードを追加します。

フラスコ_app/templates / index.html

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Books {% endblock %}</h1>
    {% for book in books %}
        <div class='book'>
            <h3>#{{ book[0] }} - {{ book[1] }} BY {{ book[2] }}</h3>
            <i><p>({{ book[3] }} pages)</p></i>
            <p class='review'>{{ book[4] }}</p>
            <i><p>Added {{ book[5] }}</p></i>
        </div>
    {% endfor %}
{% endblock %}

ファイルを保存して閉じます。

このファイルでは、ベーステンプレートを拡張し、contentブロックの内容を置き換えます。 タイトルを兼ねる<h1>の見出しを使用します。

{% for book in books %}行のJinjafor loop を使用して、booksリストの各本を調べます。 book[0]を使用して最初のアイテムであるブックIDを表示します。 次に、本のタイトル、著者、ページ数、レビュー、および本が追加された日付を表示します。

仮想環境をアクティブにしてflask_appディレクトリにいるときに、FLASK_APP環境変数を使用して、アプリケーション(この場合はapp.py)についてFlaskに通知します。 次に、FLASK_ENV環境変数をdevelopmentに設定して、アプリケーションを開発モードで実行し、デバッガーにアクセスします。 Flaskデバッガーの詳細については、Flaskアプリケーションでエラーを処理する方法を参照してください。 これを行うには、次のコマンドを使用します。

  1. export FLASK_APP=app
  2. export FLASK_ENV=development

DB_USERNAMEおよびDB_PASSWORD環境変数をまだ設定していない場合は、必ず設定してください。

  1. export DB_USERNAME="sammy"
  2. export DB_PASSWORD="password"

次に、アプリケーションを実行します。

  1. flask run

開発サーバーが実行されている状態で、ブラウザーを使用して次のURLにアクセスします。

http://127.0.0.1:5000/

最初の開始時にデータベースに追加した本が表示されます。

Index Page

データベース内の書籍をインデックスページに表示しました。 ここで、ユーザーが新しい本を追加できるようにする必要があります。 次のステップで、本を追加するための新しいルートを追加します。

ステップ5—新しい本を追加する

このステップでは、データベースに新しい本やレビューを追加するための新しいルートを作成します。

ユーザーが本のタイトル、本の著者、ページ数、書評を入力するWebフォームを含むページを追加します。

開発サーバーを実行したままにして、新しいターミナルウィンドウを開きます。

まず、app.pyファイルを開きます。

  1. nano app.py

Webフォームを処理するには、flaskパッケージからいくつかのものをインポートする必要があります。

  • 送信されたデータにアクセスするためのグローバルrequestオブジェクト。
  • url_for()関数を使用してURLを生成します。
  • redirect()関数は、データベースに本を追加した後、ユーザーをインデックスページにリダイレクトします。

これらのインポートをファイルの最初の行に追加します。

フラスコ_app/app.py

from flask import Flask, render_template, request, url_for, redirect

# ...

次に、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テンプレートを開きます。

  1. nano templates/create.html

次のコードを追加します。

フラスコ_app/templates / create.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Add a New Book {% endblock %}</h1>
    <form method="post">
        <p>
            <label for="title">Title</label>
            <input type="text" name="title"
                   placeholder="Book title">
            </input>
        </p>

        <p>
            <label for="author">Author</label>
            <input type="text" name="author"
                   placeholder="Book author">
            </input>
        </p>

        <p>
            <label for="pages_num">Number of pages</label>
            <input type="number" name="pages_num"
                   placeholder="Number of pages">
            </input>
        </p>
        <p>
        <label for="review">Review</label>
        <br>
        <textarea name="review"
                  placeholder="Review"
                  rows="15"
                  cols="60"
                  ></textarea>
        </p>
        <p>
            <button type="submit">Submit</button>
        </p>
    </form>
{% endblock %}

ファイルを保存して閉じます。

基本テンプレートを拡張し、見出しをタイトルとして設定し、属性methodpostに設定した<form>タグを使用して、フォームがPOSTリクエストを送信することを示します。

titleという名前のテキストフィールドがあり、これを使用して/createルートのタイトルデータにアクセスします。

著者用のテキストフィールド、ページ数用の番号フィールド、および書評用のテキスト領域があります。

最後に、フォームの最後に送信ボタンがあります。

これで、開発サーバーが実行されている状態で、ブラウザーを使用して/createルートに移動します。

http://127.0.0.1:5000/create

新しい本を追加ページが表示されます。このページには、本のタイトル、著者、ページ数の入力フィールド、本のレビュー用のテキスト領域、および送信ボタン。

Add a New Book

フォームに入力して送信し、サーバーにPOSTリクエストを送信しても、/createルートでPOSTリクエストを処理しなかったため、何も起こりません。

app.pyを開いて、ユーザーが送信するPOSTリクエストを処理します。

  1. nano app.py

/createルートを編集して、次のようにします。

フラスコ_app/app.py

# ...

@app.route('/create/', methods=('GET', 'POST'))
def create():
    if request.method == 'POST':
        title = request.form['title']
        author = request.form['author']
        pages_num = int(request.form['pages_num'])
        review = request.form['review']

        conn = get_db_connection()
        cur = conn.cursor()
        cur.execute('INSERT INTO books (title, author, pages_num, review)'
                    'VALUES (%s, %s, %s, %s)',
                    (title, author, pages_num, review))
        conn.commit()
        cur.close()
        conn.close()
        return redirect(url_for('index'))

    return render_template('create.html')

ファイルを保存して閉じます。

if request.method == 'POST'条件内でPOST要求を処理します。 request.formオブジェクトから、タイトル、作成者、ページ数、およびユーザーが送信したレビューを抽出します。

get_db_connection()関数を使用してデータベースを開き、カーソルを作成します。 次に、INSERT INTO SQLステートメントを実行して、タイトル、作成者、ページ数を挿入し、booksテーブルに送信されたユーザーを確認します。

トランザクションをコミットし、カーソルと接続を閉じます。

最後に、ユーザーをインデックスページにリダイレクトします。このページでは、既存の本の下に新しく追加された本が表示されます。

開発サーバーが実行されている状態で、ブラウザーを使用して/createルートに移動します。

http://127.0.0.1:5000/create

フォームにデータを入力して送信します。

新しい書評が表示されるインデックスページにリダイレクトされます。

次に、ナビゲーションバーの[作成]ページへのリンクを追加します。 base.htmlを開きます:

  1. 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>
        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }

        .book {
            padding: 20px;
            margin: 10px;
            background-color: #f7f4f4;
        }

        .review {
                margin-left: 50px;
                font-size: 20px;
        }

    </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">
        {% block content %} {% endblock %}
    </div>
</body>
</html>

ファイルを保存して閉じます。

ここでは、作成ページを指すナビゲーションバーに新しい<a>リンクを追加します。

インデックスページを更新すると、ナビゲーションバーに新しいリンクが表示されます。

これで、新しい書評を追加するためのWebフォームを含むページができました。 Webフォームの詳細については、FlaskアプリケーションでWebフォームを使用する方法を参照してください。 Webフォームを管理するためのより高度で安全な方法については、Flask-WTFを使用してWebフォームを使用および検証する方法を参照してください。

結論

PostgreSQLデータベースと通信する書評用の小さなWebアプリケーションを作成しました。 Flaskアプリケーションには、データベースへの新しいデータの追加、データの取得、ページへの表示など、基本的なデータベース機能があります。

Flaskの詳細については、Flaskシリーズの他のチュートリアルをご覧ください。