Flask-SQLAlchemyを使用してFlaskアプリケーションのデータベースと対話する方法
著者は、 Write for DOnations プログラムの一環として、 Free and Open SourceFundを選択して寄付を受け取りました。
序章
Webアプリケーションでは、通常、データの組織化されたコレクションであるデータベースが必要です。 データベースを使用して、効率的に取得および操作できる永続データを保存および維持します。 たとえば、ソーシャルメディアアプリケーションでは、ユーザーデータ(個人情報、投稿、コメント、フォロワー)が効率的に操作できる方法で保存されているデータベースがあります。 さまざまな要件や条件に応じて、データベースにデータを追加、取得、変更、または削除できます。 Webアプリケーションでは、これらの要件は、ユーザーが新しい投稿を追加したり、投稿を削除したり、アカウントを削除したりする場合があります。これにより、投稿が削除される場合と削除されない場合があります。 データを操作するために実行するアクションは、アプリケーションの特定の機能によって異なります。 たとえば、ユーザーにタイトルのない投稿を追加させたくない場合があります。
Flaskは、Python言語でWebアプリケーションを作成するための便利なツールと機能を提供する軽量のPythonWebフレームワークです。 SQLAlchemy は、リレーショナルデータベースに効率的で高性能なデータベースアクセスを提供するSQLツールキットです。 SQLite、MySQL、PostgreSQLなどのいくつかのデータベースエンジンと対話する方法を提供します。 これにより、データベースのSQL機能にアクセスできます。 また、オブジェクトリレーショナルマッパー(ORM)も提供します。これにより、単純なPythonオブジェクトとメソッドを使用してクエリを実行し、データを処理できます。 Flask-SQLAlchemy は、FlaskでSQLAlchemyを簡単に使用できるようにする、Flask拡張機能であり、SQLAlchemyを介してFlaskアプリケーションのデータベースと対話するためのツールとメソッドを提供します。
このチュートリアルでは、Flask-SQLAlchemy拡張機能の使用方法を示す小さな学生管理システムを構築します。 これをFlaskで使用して、データベースサーバーへの接続、テーブルの作成、テーブルへのデータの追加、取得、データベースからのアイテムの更新と削除などの基本的なタスクを実行します。 SQLAlchemyはSQLiteで使用しますが、PostgreSQLやMySQLなどの他のデータベースエンジンでも使用できます。 SQLiteはPythonでうまく機能します。これは、Python標準ライブラリが sqlite3モジュールを提供するためです。これは、SQLAlchemyがバックグラウンドで使用して、何もインストールせずにSQLiteデータベースと対話するために使用されます。 SQliteはデフォルトでLinuxシステムにインストールされ、WindowsのPythonパッケージの一部としてインストールされます。
前提条件
-
ローカルのPython3プログラミング環境。 Python3シリーズのローカルプログラミング環境をインストールおよびセットアップする方法のチュートリアルに従ってください。 このチュートリアルでは、プロジェクトディレクトリを
flask_app
と呼びます。 -
ルート、ビュー関数、テンプレートなどの基本的なFlaskの概念の理解。 Flaskに慣れていない場合は、FlaskとPythonを使用して最初のWebアプリケーションを作成する方法およびFlaskアプリケーションでテンプレートを使用する方法を確認してください。
-
基本的なHTMLの概念の理解。 背景知識については、HTMLを使用してWebサイトを構築する方法チュートリアルシリーズを確認できます。
ステップ1—FlaskとFlaskのインストール-SQLAlchemy
このステップでは、アプリケーションに必要なパッケージをインストールします。
仮想環境をアクティブにした状態で、pip
を使用してFlaskとFlask-SQLAlchemyをインストールします。
- pip install Flask Flask-SQLAlchemy
インストールが正常に完了すると、出力の最後に次のような行が表示されます。
OutputSuccessfully installed Flask-2.0.3 Flask-SQLAlchemy-2.5.1 Jinja2-3.0.3 MarkupSafe-2.1.0 SQLAlchemy-1.4.31 Werkzeug-2.0.3 click-8.0.4 greenlet-1.1.2 itsdangerous-2.1.0
必要なPythonパッケージがインストールされたら、次にデータベースをセットアップします。
ステップ2—データベースとモデルの設定
このステップでは、データベース接続をセットアップし、SQLAlchemy データベースモデルを作成します。これは、データを格納するテーブルを表すPythonクラスです。 データベースを開始し、宣言するモデルに基づいて学生用のテーブルを作成し、数人の学生を学生テーブルに追加します。
データベース接続の設定
flask_app
ディレクトリにあるapp.py
というファイルを開きます。 このファイルには、データベースとFlaskルートを設定するためのコードが含まれています。
- nano app.py
このファイルは、database.db
というSQLiteデータベースに接続し、Flaskルートに加えて、学生情報を格納するためのデータベース学生テーブルを表すStudent
というクラスを持ちます。 app.py
の先頭に次のimport
ステートメントを追加します。
import os
from flask import Flask, render_template, request, url_for, redirect
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.sql import func
ここでは、 osモジュールをインポートします。これにより、さまざまなオペレーティングシステムインターフェイスにアクセスできます。 これを使用して、database.db
データベースファイルのファイルパスを作成します。
次に、flask
パッケージから、アプリケーションに必要なヘルパーをインポートします。Flaskアプリケーションインスタンスを作成するためのFlask
クラス、テンプレートをレンダリングするためのrender_template()
関数、リクエストを処理するrequest
オブジェクト、ルートのURLを作成するurl_for()
関数、ユーザーをリダイレクトするredirect()
関数。 ルートとテンプレートの詳細については、Flaskアプリケーションでテンプレートを使用する方法を参照してください。
次に、Flask-SQLAlchemy拡張機能からSQLAlchemy
クラスをインポートします。これにより、ヘルパーに加えて、SQLAlchemyのすべての関数とクラス、およびFlaskをSQLAlchemyと統合する機能にアクセスできます。 これを使用して、Flaskアプリケーションに接続するデータベースオブジェクトを作成します。これにより、SQL言語を使用せずに、Pythonクラス、オブジェクト、および関数を使用してテーブルを作成および操作できます。
また、func
ヘルパーをsqlalchemy.sql
モジュールからインポートして、SQL関数にアクセスします。 学生レコードが作成されるときのデフォルトの作成日時を設定するには、学生管理システムでこれが必要になります。
インポートの下で、データベースファイルパスを設定し、Flaskアプリケーションをインスタンス化し、アプリケーションを構成してSQLAlchemyに接続します。 次のコードを追加します。
basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] =\
'sqlite:///' + os.path.join(basedir, 'database.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
ここでは、SQLiteデータベースファイルのパスを作成します。 まず、ベースディレクトリを現在のディレクトリとして定義します。 os.path.abspath()関数を使用して、現在のファイルのディレクトリの絶対パスを取得します。 特別な__file__
変数は、現在のapp.py
ファイルのパス名を保持します。 ベースディレクトリの絶対パスをbasedir
という変数に保存します。
次に、app
というFlaskアプリケーションインスタンスを作成します。これを使用して、2つのFlask-SQLAlchemy構成キーを構成します。
-
SQLALCHEMY_DATABASE_URI
:接続を確立するデータベースを指定するデータベースURI。 この場合、URIはsqlite:///path/to/database.db
の形式に従います。 os.path.join()関数を使用して、作成してbasedir
変数に格納したベースディレクトリと、database.db
ファイル名をインテリジェントに結合します。 これにより、flask_app
ディレクトリのdatabase.db
データベースファイルに接続されます。 データベースを開始すると、ファイルが作成されます。 -
SQLALCHEMY_TRACK_MODIFICATIONS
:オブジェクトの変更の追跡を有効または無効にする構成。False
に設定すると、追跡が無効になり、使用するメモリが少なくなります。 詳細については、Flask-SQLAlchemyドキュメントの構成ページを参照してください。
ノート:
PostgreSQLやMySQLなどの別のデータベースエンジンを使用する場合は、適切なURIを使用する必要があります。
PostgreSQLの場合、次の形式を使用します。
postgresql://username:password@host:port/database_name
MySQLの場合:
mysql://username:password@host:port/database_name
詳細については、SQLAlchemyのドキュメントでエンジン構成を参照してください。
データベースURIを設定し、追跡を無効にしてSQLAlchemyを構成した後、SQLAlchemy
クラスを使用してデータベースオブジェクトを作成し、アプリケーションインスタンスを渡してFlaskアプリケーションをSQLAlchemyに接続します。 データベースオブジェクトをdb
という変数に格納します。 このdb
オブジェクトを使用して、データベースを操作します。
テーブルの宣言
データベース接続が確立され、データベースオブジェクトが作成されたら、データベースオブジェクトを使用して、学生用のデータベーステーブルを作成します。このテーブルは、 model —基本クラスFlask-から継承するPythonクラスで表されます。 SQLAlchemyは、前に作成したdb
データベースインスタンスを介して提供します。 学生テーブルをモデルとして定義するには、app.py
ファイルに次のクラスを追加します。
# ...
class Student(db.Model):
id = db.Column(db.Integer, primary_key=True)
firstname = db.Column(db.String(100), nullable=False)
lastname = db.Column(db.String(100), nullable=False)
email = db.Column(db.String(80), unique=True, nullable=False)
age = db.Column(db.Integer)
created_at = db.Column(db.DateTime(timezone=True),
server_default=func.now())
bio = db.Column(db.Text)
def __repr__(self):
return f'<Student {self.firstname}>'
ここでは、db.Model
クラスを継承するStudent
モデルを作成します。 これは学生テーブルを表します。 db.Column クラスを使用して、テーブルの列を定義します。 最初の引数は列タイプを表し、追加の引数は列構成を表します。
Student
モデルには次の列を定義します。
id
:学生ID。db.Integer
を使用して整数として定義します。primary_key=True
は、この列を主キーとして定義します。これにより、各エントリ(つまり学生)のデータベースによって一意の値が割り当てられます。firstname
:学生の名。 最大長が100
文字の文字列。nullable=False
は、この列が空であってはならないことを意味します。lastname
:学生の名前。 最大長が100
文字の文字列。nullable=False
は、この列が空であってはならないことを意味します。email
:学生のメール。 最大長が80
文字の文字列。unique=True
は、各メールが生徒ごとに一意であることを意味します。nullable=False
は、この列が空であってはならないことを意味します。age
:学生の年齢。created_at
:データベースに学生レコードが作成された時刻。 db.DateTime を使用して、Python datetimeオブジェクトとして定義します。timezone=True
は、タイムゾーンのサポートを有効にします。 server_default は、テーブルの作成時にデータベースのデフォルト値を設定するため、デフォルト値はモデルではなくデータベースによって処理されます。 これに、SQLnow()
日時関数を呼び出すfunc.now()関数を渡します。 SQLiteでは、学生テーブルを作成するときにCURRENT_TIMESTAMP
としてレンダリングされます。bio
:学生の略歴。db.Text()
は、列が長いテキストを保持していることを示します。
前のコードブロックで使用したタイプ以外の列タイプについては、SQLAlchemyのドキュメントを参照してください。
特別な__repr __ 関数を使用すると、デバッグ目的で各オブジェクトを認識するための文字列表現を各オブジェクトに与えることができます。 この場合、学生の名を使用します。
app.py
ファイルは次のようになります。
import os
from flask import Flask, render_template, request, url_for, redirect
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.sql import func
basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] =\
'sqlite:///' + os.path.join(basedir, 'database.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class Student(db.Model):
id = db.Column(db.Integer, primary_key=True)
firstname = db.Column(db.String(100), nullable=False)
lastname = db.Column(db.String(100), nullable=False)
email = db.Column(db.String(80), unique=True, nullable=False)
age = db.Column(db.Integer)
created_at = db.Column(db.DateTime(timezone=True),
server_default=func.now())
bio = db.Column(db.Text)
def __repr__(self):
return f'<Student {self.firstname}>'
app.py
を保存して閉じます。
データベースの作成
データベース接続と学生モデルを設定したので、Flaskシェルを使用して、Student
モデルに基づいてデータベースと学生テーブルを作成します。
仮想環境をアクティブにした状態で、FLASK_APP
環境変数を使用して、app.py
ファイルをFlaskアプリケーションとして設定します。 次に、flask_app
ディレクトリで次のコマンドを使用してFlaskシェルを開きます。
- export FLASK_APP=app
- flask shell
Pythonインタラクティブシェルが開きます。 この特別なシェルは、Flaskアプリケーションのコンテキストでコマンドを実行するため、呼び出すFlask-SQLAlchemy関数がアプリケーションに接続されます。
データベースオブジェクトと学生モデルをインポートしてから、db.create_all()
関数を実行して、モデルに関連付けられているテーブルを作成します。 この場合、モデルは1つだけです。つまり、関数呼び出しはデータベースに1つのテーブルのみを作成します。
- from app import db, Student
- db.create_all()
シェルを実行したまま、別のターミナルウィンドウを開き、flask_app
ディレクトリに移動します。 これで、flask_app
にdatabase.db
という新しいファイルが表示されます。
ノート:
db.create_all()
関数は、テーブルがすでに存在する場合、テーブルを再作成または更新しません。 たとえば、新しい列を追加してモデルを変更し、db.create_all()
関数を実行した場合、テーブルがデータベースにすでに存在する場合、モデルに加えた変更はテーブルに適用されません。 解決策は、db.drop_all()
関数を使用して既存のデータベーステーブルをすべて削除してから、次のようにdb.create_all()
関数を使用してそれらを再作成することです。
- db.drop_all()
- db.create_all()
これにより、モデルに加えた変更が適用されますが、データベース内の既存のデータもすべて削除されます。 データベースを更新して既存のデータを保持するには、スキーマ移行を使用する必要があります。これにより、テーブルを変更してデータを保持できます。 Flask-Migrate 拡張機能を使用して、Flaskコマンドラインインターフェイスを介してSQLAlchemyスキーマの移行を実行できます。
エラーが発生した場合は、データベースURIとモデル宣言が正しいことを確認してください。
テーブルへの入力
データベースと学生テーブルを作成したら、フラスコシェルを使用して、Student
モデルを介してデータベースに学生を追加します。
以前に開いたものと同じフラスコシェルを使用するか、flask_app
ディレクトリで仮想環境をアクティブにして新しいフラスコシェルを開きます。
- flask shell
データベースに学生を追加するには、データベースオブジェクトとStudent
モデルをインポートし、Student
モデルのインスタンスを作成して、次のようにキーワード引数を介して学生データを渡します。
- from app import db, Student
- student_john = Student(firstname='john', lastname='doe',
- email='[email protected]', age=23,
- bio='Biology student')
student_john
オブジェクトは、データベースに追加される学生を表しますが、このオブジェクトはまだデータベースに書き込まれていません。 フラスコシェル内のオブジェクトをチェックして、__repr__()
メソッドで作成した表現文字列を確認します。
- student_john
次の出力が表示されます。
Output<Student john>
Student
モデルで定義したクラス属性を使用して、列の値を取得できます。
- student_john.firstname
- student_john.bio
Output'john'
'Biology student'
この学生はまだデータベースに追加されていないため、そのIDはNone
になります。
- print(student_john.id)
OutputNone
この学生をデータベースに追加するには、まず、データベーストランザクションを管理するデータベースセッションに学生を追加する必要があります。 Flask-SQLAlchemyは、データベースの変更を管理できるdb.session
オブジェクトを提供します。 student_john
オブジェクトをdb.session.add()
メソッドを使用してセッションに追加し、データベースに書き込む準備をします。
- db.session.add(student_john)
これによりINSERT
ステートメントが発行されますが、データベーストランザクションがまだコミットされていないため、IDは返されません。 トランザクションをコミットしてデータベースに変更を適用するには、db.session.commit()
メソッドを使用します。
- db.session.commit()
学生のジョンがデータベースに追加されたので、そのIDを取得できます。
- print(student_john.id)
Output1
db.session.add()
メソッドを使用して、データベース内のアイテムを編集することもできます。 たとえば、次のように学生の電子メールを変更できます。
- student_john.email = '[email protected]'
- db.session.add(student_john)
- db.session.commit()
Flaskシェルを使用して、データベースにさらに数人の学生を追加します。
- sammy = Student(firstname='Sammy',
- lastname='Shark',
- email='[email protected]',
- age=20,
- bio='Marine biology student')
-
- carl = Student(firstname='Carl',
- lastname='White',
- email='[email protected]',
- age=22,
- bio='Marine geology student')
-
- db.session.add(sammy)
- db.session.add(carl)
- db.session.commit()
これで、query
属性とall()
メソッドを使用して、studentテーブルのすべてのレコードをクエリできます。
- Student.query.all()
次の出力が表示されます。
Output[<Student john>, <Student Sammy>, <Student Carl>]
この時点で、データベースには3人の学生がいます。 次に、インデックスページのFlaskルートを作成し、データベース内のすべての学生をそのページに表示します。
ステップ3—すべてのレコードを表示する
このステップでは、データベース内のすべての学生をインデックスページに表示するためのルートとテンプレートを作成します。
Flaskシェルを実行したままにして、新しいターミナルウィンドウを開きます。
app.py
ファイルを開いて、インデックスページのルートを追加します。
- nano app.py
ファイルの最後に次のルートを追加します。
# ...
@app.route('/')
def index():
students = Student.query.all()
return render_template('index.html', students=students)
ファイルを保存して閉じます。
ここでは、app.route()
デコレータを使用してindex()
ビュー関数を作成します。 この関数では、データベースにクエリを実行し、Student
モデルとquery
属性を使用してすべての学生を取得します。これにより、さまざまな方法を使用してデータベースから1つ以上のアイテムを取得できます。 all()
メソッドを使用して、データベース内のすべての学生エントリを取得します。 クエリ結果をstudents
という変数に格納し、render_template()
ヘルパー関数を使用してレンダリングするindex.html
というテンプレートに渡します。
データベース内の既存の学生を表示するindex.html
テンプレートファイルを作成する前に、まずベーステンプレートを作成します。このテンプレートには、他のテンプレートがコードを回避するために使用するすべての基本的なHTMLコードが含まれます。繰り返し。 次に、index()
関数でレンダリングしたindex.html
テンプレートファイルを作成します。 テンプレートの詳細については、Flaskアプリケーションでテンプレートを使用する方法を参照してください。
templates
ディレクトリを作成し、base.html
という名前の新しいテンプレートを開きます。
- mkdir templates
- nano templates/base.html
base.html
ファイル内に次のコードを追加します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %} {% endblock %} - FlaskApp</title>
<style>
.title {
margin: 5px;
}
.content {
margin: 5px;
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.student {
flex: 20%;
padding: 10px;
margin: 5px;
background-color: #f3f3f3;
inline-size: 100%;
}
.bio {
padding: 10px;
margin: 5px;
background-color: #ffffff;
color: #004835;
}
.name a {
color: #00a36f;
text-decoration: none;
}
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="#">Create</a>
<a href="#">About</a>
</nav>
<hr>
<div class="content">
{% block content %} {% endblock %}
</div>
</body>
</html>
ファイルを保存して閉じます。
この基本テンプレートには、他のテンプレートで再利用する必要があるすべてのHTMLボイラープレートが含まれています。 title
ブロックは各ページのタイトルを設定するために置き換えられ、content
ブロックは各ページのコンテンツに置き換えられます。 ナビゲーションバーには3つのリンクがあります。1つはurl_for()
ヘルパー機能を使用してindex()
ビュー機能にリンクするインデックスページ用、もう1つはCreateページ用です。アプリケーションに追加することを選択した場合は、Aboutページの場合。 Create リンクを機能させるために新しい学生を作成するためのページを追加した後、このファイルを後で編集します。
次に、新しいindex.html
テンプレートファイルを開きます。 これは、app.py
ファイルで参照したテンプレートです。
- nano templates/index.html
次のコードを追加します。
{% extends 'base.html' %}
{% block content %}
<h1 class="title">{% block title %} Students {% endblock %}</h1>
<div class="content">
{% for student in students %}
<div class="student">
<p><b>#{{ student.id }}</b></p>
<b>
<p class="name">{{ student.firstname }} {{ student.lastname }}</p>
</b>
<p>{{ student.email }}</p>
<p>{{ student.age }} years old.</p>
<p>Joined: {{ student.created_at }}</p>
<div class="bio">
<h4>Bio</h4>
<p>{{ student.bio }}</p>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
ファイルを保存して閉じます。
ここでは、ベーステンプレートを拡張し、コンテンツブロックのコンテンツを置き換えます。 タイトルを兼ねる<h1>
の見出しを使用します。 {% for student in students %}
行のJinjafor loop を使用して、index()
ビュー関数からこれに渡したstudents
変数の各学生を調べます。レンプレート。 学生ID、名前と名前、電子メール、年齢、データベースに追加された日付、および経歴を表示します。
仮想環境をアクティブにしてflask_app
ディレクトリにいるときに、FLASK_APP
環境変数を使用して、アプリケーション(この場合はapp.py
)についてFlaskに通知します。 次に、FLASK_ENV
環境変数をdevelopment
に設定して、アプリケーションを開発モードで実行し、デバッガーにアクセスします。 Flaskデバッガーの詳細については、Flaskアプリケーションでエラーを処理する方法を参照してください。 これを行うには、次のコマンドを使用します。
- export FLASK_APP=app
- export FLASK_ENV=development
次に、アプリケーションを実行します。
- flask run
開発サーバーが実行されている状態で、ブラウザーを使用して次のURLにアクセスします。
http://127.0.0.1:5000/
次のようなページに、データベースに追加した学生が表示されます。
データベースにある生徒をインデックスページに表示しました。 次に、個々の学生の詳細を表示できる学生ページのルートを作成します。
ステップ4—単一のレコードを表示する
このステップでは、Flaskシェルを使用して、IDで生徒にクエリを実行し、専用ページに各生徒の詳細を表示するルートとテンプレートを作成します。
このステップが終了すると、URL http://127.0.0.1:5000/1
は最初の学生を表示するページになります(IDが1
であるため)。 URL http://127.0.0.1:5000/ID
は、関連するID
番号(存在する場合)を含む投稿を表示します。
開発サーバーを実行したままにして、新しいターミナルウィンドウを開きます。
生徒にクエリを実行する方法のデモンストレーションについては、Flaskシェルを開きます。
- flask shell
レコードをクエリしてデータベースからデータを取得するために、Flask-SQLAlchemyはモデルクラスにquery
属性を提供します。 そのメソッドを使用して、特定のフィルターでレコードを取得できます。
たとえば、filter_by()
メソッドを、firstname
などのパラメーターとともに使用して、特定の学生を取得するための引数を持つテーブルの列に一致させることができます。
- from app import db, Student
- Student.query.filter_by(firstname='Sammy').all()
Output[<Student Sammy>]
ここでは、Sammy
を名としてすべての学生を取得します。 all()
メソッドを使用して、すべての結果のリストを取得します。 ここで唯一の結果である最初の結果を取得するには、first()
メソッドを使用できます。
- Student.query.filter_by(firstname='Sammy').first()
Output<Student Sammy>
IDで学生を取得するには、filter_by(id=ID)
を使用できます。
- Student.query.filter_by(id=3).first()
または、短いget()
メソッドを使用できます。これにより、主キーを使用して特定のアイテムを取得できます。
- Student.query.get(3)
どちらも同じ出力になります。
Output<Student Carl>
これで、シェルを終了できます。
- exit()
IDで生徒を取得するには、個々の生徒ごとにページをレンダリングする新しいルートを作成します。 Flask-SQLAlchemyが提供するget_or_404()
メソッドを使用します。これは、get()
メソッドの変形です。 違いは、get()
は、指定されたIDに一致する結果がない場合に値None
を返し、get_or_404()
は404 Not Found
HTTP応答を返すことです。 変更するには、app.py
を開きます。
- nano app.py
ファイルの最後に次のルートを追加します。
# ...
@app.route('/<int:student_id>/')
def student(student_id):
student = Student.query.get_or_404(student_id)
return render_template('student.html', student=student)
ファイルを保存して閉じます。
ここでは、ルート'/<int:student_id>/'
を使用します。ここで、int:
は、URLのデフォルトの文字列を整数に変換するコンバーターです。 また、student_id
は、ページに表示する生徒を決定するURL変数です。
IDは、student_id
パラメーターを介してURLからstudent()
ビュー関数に渡されます。 関数内で、学生コレクションをクエリし、get_or_404()
メソッドを使用してIDで学生を取得します。 これにより、学生データが存在する場合はstudent
変数に保存され、指定されたIDの学生がデータベースに存在しない場合は404 Not Found
HTTPエラーで応答します。
student.html
というテンプレートをレンダリングし、取得した生徒に渡します。
この新しいstudent.html
テンプレートファイルを開きます。
- nano templates/student.html
この新しいstudent.html
ファイルに次のコードを入力します。 これはindex.html
テンプレートに似ていますが、1人の生徒しか表示されない点が異なります。
{% extends 'base.html' %}
{% block content %}
<span class="title">
<h1>{% block title %} {{ student.firstname }} {{ student.lastname }}{% endblock %}</h1>
</span>
<div class="content">
<div class="student">
<p><b>#{{ student.id }}</b></p>
<b>
<p class="name">{{ student.firstname }} {{ student.lastname }}</p>
</b>
<p>{{ student.email }}</p>
<p>{{ student.age }} years old.</p>
<p>Joined: {{ student.created_at }}</p>
<div class="bio">
<h4>Bio</h4>
<p>{{ student.bio }}</p>
</div>
</div>
</div>
{% endblock %}
ファイルを保存して閉じます。
このファイルでは、基本テンプレートを拡張して、ページのタイトルとして学生のフルネームを設定します。 学生ID、学生の姓名、電子メール、年齢、レコード作成日、およびその経歴を表示します。
ブラウザを使用して、2番目の学生のURLに移動します。
http://127.0.0.1:5000/2
次のようなページが表示されます。
次に、index.html
を編集して、各生徒の名前を自分のページにリンクさせます。
- nano templates/index.html
for
ループを編集して、次のようにします。
{% for student in students %}
<div class="student">
<p><b>#{{ student.id }}</b></p>
<b>
<p class="name">
<a href="{{ url_for('student', student_id=student.id)}}">
{{ student.firstname }} {{ student.lastname }}
</a>
</p>
</b>
<p>{{ student.email }}</p>
<p>{{ student.age }} years old.</p>
<p>Joined: {{ student.created_at }}</p>
<div class="bio">
<h4>Bio</h4>
<p>{{ student.bio }}</p>
</div>
</div>
{% endfor %}
ファイルを保存して閉じます。
url_for()
関数を使用して学生ページにリンクする<a>
タグを学生のフルネームに追加し、student.id
に保存されている学生IDを
インデックスページに移動するか、更新します。
http://127.0.0.1:5000/
これで、各生徒の名前が適切な生徒のページにリンクしていることがわかります。
個々の学生用のページを作成したら、次に、データベースに新しい学生を追加するためのページを追加します。
ステップ5—新しいレコードを作成する
このステップでは、Webフォームを使用してデータベースに新しい学生を追加するための新しいルートをアプリケーションに追加します。
ユーザーが学生のデータを入力するWebフォームを使用してページをレンダリングします。 次に、フォームの送信を処理し、Student
モデルを使用して新しい学生のオブジェクトを作成し、それをセッションに追加してから、手順2で学生のエントリを追加したのと同じようにトランザクションをコミットします。
開発サーバーを実行したままにして、新しいターミナルウィンドウを開きます。
まず、app.py
ファイルを開きます。
- nano app.py
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
次のコードを追加します。
{% extends 'base.html' %}
{% block content %}
<h1 style="width: 100%">{% block title %} Add a New Student {% endblock %}</h1>
<form method="post">
<p>
<label for="firstname">First Name</label>
<input type="text" name="firstname"
placeholder="First name">
</input>
</p>
<p>
<label for="lastname">Last Name</label>
<input type="text" name="lastname"
placeholder="Last name">
</input>
</p>
<p>
<label for="email">Email</label>
<input type="email" name="email"
placeholder="Student email">
</input>
</p>
<p>
<label for="age">Age</label>
<input type="number" name="age"
placeholder="Age">
</input>
</p>
<p>
<label for="bio">Bio</label>
<br>
<textarea name="bio"
placeholder="Bio"
rows="15"
cols="60"
></textarea>
</p>
<p>
<button type="submit">Submit</button>
</p>
</form>
{% endblock %}
ファイルを保存して閉じます。
基本テンプレートを拡張し、見出しをタイトルとして設定し、属性method
をpost
に設定した<form>
タグを使用して、フォームがPOSTリクエストを送信することを示します。
firstname
とlastname
という名前の2つのテキストフィールドがあります。 これらの名前を使用して、後でユーザーがビュー機能で送信するフォームデータにアクセスします。
email
という名前の電子メールフィールド、学生の年齢の番号フィールド、および学生の略歴のテキスト領域があります。
最後に、フォームの最後に送信ボタンがあります。
これで、開発サーバーが実行されている状態で、ブラウザーを使用して/create
ルートに移動します。
http://127.0.0.1:5000/create
新しい学生を追加ページが表示されます。このページには、次のようなWebフォームと送信ボタンがあります。
フォームに入力して送信し、サーバーにPOSTリクエストを送信しても、/create
ルートでPOSTリクエストを処理しなかったため、何も起こりません。
app.py
を開いて、ユーザーが送信するPOSTリクエストを処理します。
- nano app.py
/create
ルートを編集して、次のようにします。
@app.route('/create/', methods=('GET', 'POST'))
def create():
if request.method == 'POST':
firstname = request.form['firstname']
lastname = request.form['lastname']
email = request.form['email']
age = int(request.form['age'])
bio = request.form['bio']
student = Student(firstname=firstname,
lastname=lastname,
email=email,
age=age,
bio=bio)
db.session.add(student)
db.session.commit()
return redirect(url_for('index'))
return render_template('create.html')
ファイルを保存して閉じます。
if request.method == 'POST'
条件内でPOST要求を処理します。 request.form
オブジェクトから、ユーザーが送信した名、姓、電子メール、年齢、および略歴を抽出します。 int() Python関数を使用して、文字列として渡される年齢を整数に変換します。 Student
モデルを使用して、student
オブジェクトを作成します。 学生オブジェクトをデータベースセッションに追加してから、トランザクションをコミットします。
最後に、ユーザーをインデックスページにリダイレクトします。このページでは、既存の学生の下に新しく追加された学生が表示されます。
開発サーバーが実行されている状態で、ブラウザーを使用して/create
ルートに移動します。
http://127.0.0.1:5000/create
フォームにデータを入力して送信します。
新しく追加された生徒が表示されるインデックスページにリダイレクトされます。
新しい生徒を追加する機能ができたので、ナビゲーションバーのCreateページへのリンクを追加する必要があります。 base.html
を開きます:
- nano templates/base.html
Create
リンクのhref
属性の値を変更して、<body>
タグを編集します。
<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>
ファイルを保存して閉じます。
インデックスページを更新すると、ナビゲーションバーのCreateリンクが機能するようになります。
これで、新しい学生を追加するためのWebフォームを含むページができました。 Webフォームの詳細については、FlaskアプリケーションでWebフォームを使用する方法を参照してください。 Webフォームを管理するためのより高度で安全な方法については、Flask-WTFを使用してWebフォームを使用および検証する方法を参照してください。 次に、既存の学生のデータを編集するためのページを追加します。
ステップ6—レコードの編集
このステップでは、既存の学生データを編集するための新しいページをアプリケーションに追加します。 新しい/ID/edit/
ルートを追加して、IDに基づいて学生のデータを編集します。
app.py
を開きます:
- nano app.py
ファイルの最後に次のルートを追加します。 これにより、IDを使用して編集する学生エントリが取得されます。 後で作成するWebフォームを介して送信された新しい学生データを抽出します。 次に、学生データを編集し、ユーザーをインデックスページにリダイレクトします。
# ...
@app.route('/<int:student_id>/edit/', methods=('GET', 'POST'))
def edit(student_id):
student = Student.query.get_or_404(student_id)
if request.method == 'POST':
firstname = request.form['firstname']
lastname = request.form['lastname']
email = request.form['email']
age = int(request.form['age'])
bio = request.form['bio']
student.firstname = firstname
student.lastname = lastname
student.email = email
student.age = age
student.bio = bio
db.session.add(student)
db.session.commit()
return redirect(url_for('index'))
return render_template('edit.html', student=student)
ファイルを保存して閉じます。
ここでは、POSTメソッドとGETメソッドの両方を受け入れるルート/<int:student_id>/edit/
があり、IDをedit()
ビュー関数に渡すURL変数としてstudent_id
があります。
Student
モデルでget_or_404()
クエリメソッドを使用して、指定された学生IDに関連付けられた学生を取得します。 指定されたIDの学生がデータベースに存在しない場合、これは404 Not Found
エラーで応答します。
指定されたIDに学生が関連付けられている場合、コードの実行はif request.method == 'POST'
条件まで続行されます。 リクエストがGETリクエストであった場合、つまりユーザーがフォームを送信しなかった場合、この条件はfalseであり、その中のコードはreturn render_template('edit.html', student=student)
行にスキップされます。 これにより、edit.html
テンプレートがレンダリングされ、データベースから取得した学生オブジェクトが渡され、学生のWebフォームに現在の学生データを入力できるようになります。 このedit.html
テンプレートは後で作成します。
ユーザーが学生データを編集してフォームを送信すると、if request.method == 'POST'
内のコードが実行されます。 送信された学生データをrequest.form
オブジェクトから対応する変数に抽出します。 手順2で行ったように、student
オブジェクトの各属性を新しく送信されたデータに設定して、列の値を変更します。 Webフォームのフィールドで変更が実行されなかった場合、その列の値はデータベース内で同じままになります。
学生データを新しく送信されたデータに設定した後、student
オブジェクトをデータベースセッションに追加し、変更をコミットします。 最後に、ユーザーをインデックスページにリダイレクトします。
次に、ユーザーが編集できるページを作成する必要があります。 新しいedit.html
テンプレートを開きます。
- nano templates/edit.html
この新しいファイルには、create.html
ファイルにあるものと同様のWebフォームがあり、フィールドのデフォルト値として現在の学生データが含まれています。 その中に次のコードを追加します。
{% extends 'base.html' %}
{% block content %}
<h1 style="width: 100%">
{% block title %} Edit {{ student.firstname }}
{{ student.lastname }}'s Details
{% endblock %}
</h1>
<form method="post">
<p>
<label for="firstname">First Name</label>
<input type="text" name="firstname"
value={{ student.firstname }}
placeholder="First name">
</input>
</p>
<p>
<label for="lastname">Last Name</label>
<input type="text" name="lastname"
value={{ student.lastname }}
placeholder="Last name">
</input>
</p>
<p>
<label for="email">Email</label>
<input type="email" name="email"
value={{ student.email }}
placeholder="Student email">
</input>
</p>
<p>
<label for="age">Age</label>
<input type="number" name="age"
value={{ student.age }}
placeholder="Age">
</input>
</p>
<p>
<label for="bio">Bio</label>
<br>
<textarea name="bio"
placeholder="Bio"
rows="15"
cols="60"
>{{ student.bio }}</textarea>
</p>
<p>
<button type="submit">Submit</button>
</p>
</form>
{% endblock %}
ファイルを保存して閉じます。
タイトルには、学生の名前と名前があります。 各入力フィールドのvalue
属性とバイオテキスト領域の値は、edit()
ビュー関数から[に渡したstudent
オブジェクトの対応する値に設定されます。 X189X]