Flask-SQLAlchemyで1対多のデータベース関係を使用する方法
序章
Flask は、Python言語でWebアプリケーションを作成するための便利なツールと機能を提供する軽量のPythonWebフレームワークです。 SQLAlchemy は、リレーショナルデータベースに効率的で高性能なデータベースアクセスを提供するSQLツールキットです。 SQLite、MySQL、PostgreSQLなどのいくつかのデータベースエンジンと対話する方法を提供します。 これにより、データベースのSQL機能にアクセスできます。 また、オブジェクトリレーショナルマッパー(ORM)も提供します。これにより、単純なPythonオブジェクトとメソッドを使用してクエリを作成し、データを処理できます。 Flask-SQLAlchemy は、FlaskでSQLAlchemyを簡単に使用できるようにする、Flask拡張機能であり、SQLAlchemyを介してFlaskアプリケーションのデータベースと対話するためのツールとメソッドを提供します。
1対多のデータベース関係は、あるテーブルのレコードが別のテーブルの複数のレコードを参照できる2つのデータベーステーブル間の関係です。 たとえば、ブログアプリケーションでは、投稿を保存するためのテーブルは、コメントを保存するためのテーブルと1対多の関係を持つことができます。 各投稿は多くのコメントを参照でき、各コメントは1つの投稿を参照します。 したがって、1つの投稿には多くのコメントとの関係があります。 postテーブルは親テーブルであり、commentsテーブルは子テーブルです。親テーブルのレコードは、子テーブルの多くのレコードを参照できます。 この関係は、各テーブルの関連データにアクセスできるようにするために重要です。
このチュートリアルでは、Flask-SQLAlchemy拡張機能を使用して1対多の関係を構築する方法を示す小さなブログシステムを構築します。 投稿とコメントの間に関係を作成します。各ブログ投稿には複数のコメントを含めることができます。
前提条件
-
ローカルの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.1.1 Flask-SQLAlchemy-2.5.1 Jinja2-3.1.1 MarkupSafe-2.1.1 SQLAlchemy-1.4.35 Werkzeug-2.1.1 click-8.1.2 greenlet-1.1.2 itsdangerous-2.1.2
必要なPythonパッケージがインストールされたら、次にデータベースをセットアップします。
ステップ2—データベースとモデルの設定
このステップでは、データベースをセットアップし、SQLAlchemyデータベースモデル—データベーステーブルを表すPythonクラスを作成します。 ブログ投稿のモデルとコメントのモデルを作成します。 データベースを開始し、投稿用のテーブルを作成し、宣言するモデルに基づいてコメント用のテーブルを追加します。 また、データベースにいくつかの投稿とコメントを挿入します。
データベース接続の設定
というファイルを開きます app.py
あなたの中で flask_app
ディレクトリ。 このファイルには、データベースとFlaskルートを設定するためのコードが含まれています。
- nano app.py
このファイルは、と呼ばれるSQLiteデータベースに接続します database.db
、および2つのクラスがあります。 Post
これはデータベース投稿テーブルを表し、 Comment
コメントテーブルを表すクラス。 このファイルには、Flaskルートも含まれます。 以下を追加します import
上部のステートメント app.py
:
import os
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
ここでは、 osモジュールをインポートします。これにより、さまざまなオペレーティングシステムインターフェイスにアクセスできます。 これを使用して、ファイルパスを作成します database.db
データベースファイル。
から flask
パッケージの場合、アプリケーションに必要なヘルパーをインポートします。 Flask
Flaskアプリケーションインスタンスを作成するクラス、 render_template()
テンプレートをレンダリングする機能、 request
リクエストを処理するオブジェクト、 url_for()
ルートのURLを作成する関数、および redirect()
ユーザーをリダイレクトするための関数。 ルートとテンプレートの詳細については、Flaskアプリケーションでテンプレートを使用する方法を参照してください。
次に、 SQLAlchemy
Flask-SQLAlchemy拡張機能のクラス。ヘルパーに加えて、SQLAlchemyのすべての関数とクラス、およびFlaskをSQLAlchemyと統合する機能にアクセスできます。 これを使用して、Flaskアプリケーションに接続するデータベースオブジェクトを作成します。これにより、SQL言語を使用せずに、Pythonクラス、オブジェクト、および関数を使用してテーブルを作成および操作できます。
インポートの下で、データベースファイルパスを設定し、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
.
次に、Flaskアプリケーションインスタンスを作成します。 app
、2つのFlask-SQLAlchemy構成キーを構成するために使用します。
-
SQLALCHEMY_DATABASE_URI
:接続を確立するデータベースを指定するデータベースURI。 この場合、URIは次の形式に従いますsqlite:///path/to/database.db
. os.path.join()関数を使用して、作成して保存したベースディレクトリにインテリジェントに参加します。basedir
変数、およびdatabase.db
ファイル名。 これはに接続しますdatabase.db
あなたのデータベースファイルflask_app
ディレクトリ。 データベースを開始すると、ファイルが作成されます。 -
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-SQLAlchemyが提供する基本クラスから継承するPythonクラス db
以前に作成したデータベースインスタンス。 投稿テーブルとコメントテーブルをモデルとして定義するには、次の2つのクラスを app.py
ファイル:
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100))
content = db.Column(db.Text)
comments = db.relationship('Comment', backref='post')
def __repr__(self):
return f'<Post "{self.title}">'
class Comment(db.Model):
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.Text)
post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
def __repr__(self):
return f'<Comment "{self.content[:20]}...">'
ここでは、 Post
モデルと Comment
から継承するモデル db.Model
クラス。
The Post
モデルはポストテーブルを表します。 db.Column クラスを使用して、その列を定義します。 最初の引数は列タイプを表し、追加の引数は列構成を表します。
次の列を定義します Post
モデル:
id
:投稿ID。 あなたはそれを整数として定義しますdb.Integer
.primary_key=True
この列を主キーとして定義します。これにより、データベースによって各エントリ(つまり、各投稿)に一意の値が割り当てられます。title
:投稿のタイトル。 最大長が100文字の文字列。content
:投稿の内容。db.Text
列が長いテキストを保持していることを示します。
The comments
クラス属性は、 Post
モデルと Comment
モデル。 あなたは db.relationship()
メソッド、コメントモデルの名前を渡します(Comment
この場合)。 あなたは backref
列のように動作する後方参照を追加するパラメータ Comment
モデル。 このようにして、コメントが投稿された投稿にアクセスできます。 post
属性。 たとえば、という変数にコメントオブジェクトがある場合 comment
、コメントが属する投稿にアクセスできるようになります comment.post
. これを示す例は後で表示されます。
前のコードブロックで使用したタイプ以外の列タイプについては、SQLAlchemyのドキュメントを参照してください。
特別な__repr __ 関数を使用すると、デバッグ目的で各オブジェクトを認識するための文字列表現を各オブジェクトに与えることができます。
The Comment
モデルはコメントテーブルを表します。 次の列を定義します。
id
:コメントID。 あなたはそれを整数として定義しますdb.Integer
.primary_key=True
この列を主キーとして定義します。これにより、データベースによって各エントリ(つまり、各コメント)に一意の値が割り当てられます。content
:コメントの内容。db.Text
列が長いテキストを保持していることを示します。post_id
:を使用して作成する整数外部キーdb.ForeignKey()
クラス。テーブルの主キーを使用して、テーブルを別のテーブルにリンクするキーです。 これにより、投稿の主キーであるIDを使用して、コメントが投稿にリンクされます。 ここでは、post
tableは親テーブルであり、各投稿に多くのコメントがあることを示しています。 Thecomment
テーブルは子テーブルです。 各コメントは、投稿のIDを使用して親投稿に関連付けられています。 したがって、各コメントにはpost_id
コメントが投稿された投稿にアクセスするために使用できる列。
の特別な__repr__関数 Comment
モデルは、コメントオブジェクトに短い文字列表現を与えるために、コメントのコンテンツの最初の20文字を表示します。
The app.py
ファイルは次のようになります。
import os
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import 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)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100))
content = db.Column(db.Text)
comments = db.relationship('Comment', backref='post')
def __repr__(self):
return f'<Post "{self.title}">'
class Comment(db.Model):
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.Text)
post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
def __repr__(self):
return f'<Comment "{self.content[:20]}...">'
保存して閉じます app.py
.
データベースの作成
データベース接続と投稿およびコメントモデルを設定したので、Flaskシェルを使用して、宣言したモデルに基づいてデータベースと投稿およびコメントテーブルを作成します。
仮想環境をアクティブにして、 app.py
を使用してFlaskアプリケーションとしてファイル FLASK_APP
環境変数:
- export FLASK_APP=app
次に、次のコマンドを使用してFlaskシェルを開きます。 flask_app
ディレクトリ:
- flask shell
Pythonインタラクティブシェルが開きます。 この特別なシェルは、Flaskアプリケーションのコンテキストでコマンドを実行するため、呼び出すFlask-SQLAlchemy関数がアプリケーションに接続されます。
データベースオブジェクトと投稿モデルおよびコメントモデルをインポートしてから、 db.create_all()
モデルに関連付けられているテーブルを作成する関数:
- from app import db, Post, Comment
- db.create_all()
シェルを実行したままにして、別のターミナルウィンドウを開き、次の場所に移動します。 flask_app
ディレクトリ。 これで、という新しいファイルが表示されます。 database.db
の flask_app
.
注: db.create_all()
テーブルがすでに存在する場合、関数はテーブルを再作成または更新しません。 たとえば、新しい列を追加してモデルを変更し、 db.create_all()
関数の場合、テーブルがデータベースにすでに存在する場合、モデルに加えた変更はテーブルに適用されません。 解決策は、既存のデータベーステーブルをすべて削除することです。 db.drop_all()
関数を作成してから、 db.create_all()
そのように機能します:
- db.drop_all()
- db.create_all()
これにより、モデルに加えた変更が適用されますが、データベース内の既存のデータもすべて削除されます。 データベース構造を更新して既存のデータを保持するには、スキーマ移行を使用する必要があります。これにより、テーブルを変更してデータを保持できます。 Flask-Migrate 拡張機能を使用して、Flaskコマンドラインインターフェイスを介してSQLAlchemyスキーマの移行を実行できます。
エラーが発生した場合は、データベースURIとモデル宣言が正しいことを確認してください。
テーブルへの入力
データベースと投稿テーブルおよびコメントテーブルを作成したら、ファイルを作成します。 flask_app
データベースに投稿やコメントを追加するためのディレクトリ。
と呼ばれる新しいファイルを開きます init_db.py
:
- nano init_db.py
次のコードを追加します。 このファイルは、3つの投稿オブジェクトと4つのコメントオブジェクトを作成し、それらをデータベースに追加します。
from app import db, Post, Comment
post1 = Post(title='Post The First', content='Content for the first post')
post2 = Post(title='Post The Second', content='Content for the Second post')
post3 = Post(title='Post The Third', content='Content for the third post')
comment1 = Comment(content='Comment for the first post', post=post1)
comment2 = Comment(content='Comment for the second post', post=post2)
comment3 = Comment(content='Another comment for the second post', post_id=2)
comment4 = Comment(content='Another comment for the first post', post_id=1)
db.session.add_all([post1, post2, post3])
db.session.add_all([comment1, comment2, comment3, comment4])
db.session.commit()
ファイルを保存して閉じます。
ここでは、データベースオブジェクトをインポートします。 Post
モデル、および Comment
からのモデル app.py
ファイル。
を使用していくつかの投稿オブジェクトを作成します Post
モデル、投稿のタイトルを title
パラメータと投稿の内容を content
パラメータ。
次に、コメントのコンテンツを渡して、いくつかのコメントオブジェクトを作成します。 コメントをそれが属する投稿に関連付けるために使用できる2つの方法があります。 投稿オブジェクトをに渡すことができます post
に示されているパラメータ comment1
と comment2
オブジェクト。 また、投稿IDをに渡すこともできます post_id
に示されているように、パラメータ comment3
と comment4
オブジェクト。 したがって、コードにpostオブジェクトがない場合は、postの整数IDを渡すことができます。
投稿オブジェクトとコメントオブジェクトを定義した後、 db.session.add_all()
トランザクションを管理するデータベースセッションにすべての投稿オブジェクトとコメントオブジェクトを追加します。 次に、 db.session.commit()
トランザクションをコミットし、データベースに変更を適用するメソッド。 SQLAlchemyデータベースセッションの詳細については、FlaskアプリケーションチュートリアルでFlask-SQLAlchemyを使用してデータベースと対話する方法のステップ2を参照してください。
を実行します init_db.py
コードを実行し、データベースにデータを追加するファイル:
- python init_db.py
データベースに追加したデータを確認するには、フラスコシェルを開いてすべての投稿をクエリし、それらのタイトルと各投稿のコメントの内容を表示します。
- flask shell
次のコードを実行します。 これにより、すべての投稿がクエリされ、各投稿のタイトルとその下の各投稿のコメントが表示されます。
from app import Post
posts = Post.query.all()
for post in posts:
print(f'## {post.title}')
for comment in post.comments:
print(f'> {comment.content}')
print('----')
ここでは、 Post
からのモデル app.py
ファイル。 データベースに存在するすべての投稿をクエリします。 all()
上のメソッド query
属性を指定し、結果をという変数に保存します posts
. 次に、 for
ループして各アイテムを確認します posts
変数。 タイトルを印刷してから、別のタイトルを使用します for
投稿に属する各コメントを確認するためにループします。 を使用して投稿のコメントにアクセスします post.comments
. コメントの内容を印刷してから、文字列を印刷します '----'
投稿を分離します。
次の出力が得られます。
Output
## Post The First
> Comment for the first post
> Another comment for the first post
----
## Post The Second
> Comment for the second post
> Another comment for the second post
----
## Post The Third
----
ご覧のとおり、ごくわずかなコードで各投稿のデータと各投稿のコメントにアクセスできます。
次に、シェルを終了します。
- exit()
この時点で、データベースにいくつかの投稿とコメントがあります。 次に、インデックスページのFlaskルートを作成し、データベース内のすべての投稿をそのページに表示します。
ステップ3—すべての投稿を表示する
このステップでは、データベース内のすべての投稿をインデックスページに表示するためのルートとテンプレートを作成します。
あなたの app.py
インデックスページのルートを追加するファイル:
- nano app.py
ファイルの最後に次のルートを追加します。
# ...
@app.route('/')
def index():
posts = Post.query.all()
return render_template('index.html', posts=posts)
ファイルを保存して閉じます。
ここでは、 index()
を使用した表示機能 app.route()
デコレータ。 この関数では、前の手順で行ったように、データベースにクエリを実行してすべての投稿を取得します。 クエリ結果をという変数に格納します posts
そして、あなたはそれをに渡します index.html
を使用してレンダリングするテンプレートファイル render_template()
ヘルパー関数。
作成する前に index.html
データベース内の既存の投稿を表示するテンプレートファイルでは、最初にベーステンプレートを作成します。このテンプレートには、コードの繰り返しを避けるために他のテンプレートでも使用されるすべての基本的なHTMLコードが含まれます。 次に、を作成します index.html
レンダリングしたテンプレートファイル index()
関数。 テンプレートの詳細については、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;
}
.comment {
padding: 10px;
margin: 10px;
background-color: #fff;
}
.post {
flex: 20%;
padding: 10px;
margin: 5px;
background-color: #f3f3f3;
inline-size: 100%;
}
.title 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="#">Comments</a>
<a href="#">About</a>
</nav>
<hr>
<div class="content">
{% block content %} {% endblock %}
</div>
</body>
</html>
ファイルを保存して閉じます。
この基本テンプレートには、他のテンプレートで再利用する必要があるすべてのHTMLボイラープレートが含まれています。 The title
各ページのタイトルを設定するためにブロックが置き換えられ、 content
ブロックは各ページのコンテンツに置き換えられます。 ナビゲーションバーには3つのリンクがあります。1つはインデックスページ用で、 index()
を使用した表示機能 url_for()
ヘルパー関数。1つはコメントページ用で、もう1つはバージョン情報ページ用です(アプリケーションに追加する場合)。 コメントリンクを機能させるために最新のコメントをすべて表示するページを追加した後、このファイルを後で編集します。
次に、新しい index.html
テンプレートファイル。 これは、で参照したテンプレートです。 app.py
ファイル:
- nano templates/index.html
次のコードを追加します。
{% extends 'base.html' %}
{% block content %}
<span class="title"><h1>{% block title %} Posts {% endblock %}</h1></span>
<div class="content">
{% for post in posts %}
<div class="post">
<p><b>#{{ post.id }}</b></p>
<b>
<p class="title">
<a href="#">
{{ post.title }}
</a>
</p>
</b>
<div class="content">
<p>{{ post.content }}</p>
</div>
<hr>
</div>
{% endfor %}
</div>
{% endblock %}
ファイルを保存して閉じます。
ここでは、ベーステンプレートを拡張し、コンテンツブロックのコンテンツを置き換えます。 あなたは <h1>
タイトルを兼ねる見出し。 行でJinjaforloopを使用します {% for post in posts %}
の各投稿を確認するには posts
から渡した変数 index()
このテンプレートに関数を表示します。 投稿ID、タイトル、投稿内容を表示します。 投稿のタイトルは、後で個々の投稿とそのコメントを表示するページにリンクされます。
あなたの中に flask_app
仮想環境がアクティブ化されているディレクトリ、アプリケーションについてFlaskに通知します(app.py
この場合)を使用して FLASK_APP
環境変数。 次に、 FLASK_ENV
環境変数から development
アプリケーションを開発モードで実行し、デバッガーにアクセスします。 Flaskデバッガーの詳細については、Flaskアプリケーションでエラーを処理する方法を参照してください。 これを行うには、次のコマンドを使用します。
- export FLASK_APP=app
- export FLASK_ENV=development
次に、アプリケーションを実行します。
- flask run
開発サーバーが実行されている状態で、ブラウザーを使用して次のURLにアクセスします。
http://127.0.0.1:5000/
次のようなページに、データベースに追加した投稿が表示されます。
データベースにある投稿をインデックスページに表示しました。 次に、投稿ページのルートを作成します。ここには、各投稿の詳細とその下のコメントが表示されます。
ステップ4—単一の投稿とそのコメントを表示する
このステップでは、ルートとテンプレートを作成して、各投稿の詳細を専用ページに表示し、その下に投稿のコメントを表示します。
このステップの終わりまでに、URL http://127.0.0.1:5000/1
最初の投稿を表示するページになります(IDがあるため) 1
)とそのコメント。 URL http://127.0.0.1:5000/ID
関連付けられた投稿が表示されます ID
番号(存在する場合)。
開発サーバーを実行したままにして、新しいターミナルウィンドウを開きます。
開ける app.py
変更の場合:
- nano app.py
ファイルの最後に次のルートを追加します。
# ...
@app.route('/<int:post_id>/')
def post(post_id):
post = Post.query.get_or_404(post_id)
return render_template('post.html', post=post)
ファイルを保存して閉じます。
ここでは、ルートを使用します '/<int:post_id>/'
、 と int:
URLのデフォルトの文字列を整数に変換するconverterです。 post_id
ページに表示する投稿を決定するURL変数です。
IDはURLからに渡されます post()
を介して機能を表示 post_id
パラメータ。 関数内で、投稿テーブルをクエリし、を使用してIDで投稿を取得します。 get_or_404()
方法。 これにより、投稿データが post
存在する場合は変数であり、 404 Not Found
指定されたIDの投稿がデータベースに存在しない場合のHTTPエラー。
と呼ばれるテンプレートをレンダリングします post.html
取得した投稿を渡します。
この新しいを開く post.html
テンプレートファイル:
- nano templates/post.html
次のコードを入力します。 これは、 index.html
テンプレート。ただし、単一の投稿のみが表示されます。
{% extends 'base.html' %}
{% block content %}
<span class="title"><h1>{% block title %} {{ post.title }} {% endblock %}</h1></span>
<div class="content">
<div class="post">
<p><b>#{{ post.id }}</b></p>
<b>
<p class="title">{{ post.title }}</p>
</b>
<div class="content">
<p>{{ post.content }}</p>
</div>
<hr>
<h3>Comments</h3>
{% for comment in post.comments %}
<div class="comment">
<p>#{{ comment.id }}</p>
<p>{{ comment.content }}</p>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
ファイルを保存して閉じます。
ここでは、基本テンプレートを拡張し、投稿タイトルをページタイトルとして設定し、投稿ID、投稿タイトル、および投稿コンテンツを表示します。 次に、経由で利用可能な投稿コメントを確認します post.comments
. コメントIDとコメントの内容を表示します。
ブラウザを使用して、2番目の投稿のURLに移動します。
http://127.0.0.1:5000/2/
次のようなページが表示されます。
次に、編集 index.html
投稿のタイトルを個々の投稿にリンクするには:
- nano templates/index.html
の値を編集します href
内の投稿タイトルのリンクの属性 for
ループ:
...
{% for post in posts %}
<div class="post">
<p><b>#{{ post.id }}</b></p>
<b>
<p class="title">
<a href="{{ url_for('post', post_id=post.id)}}">
{{ post.title }}
</a>
</p>
</b>
<div class="content">
<p>{{ post.content }}</p>
</div>
<hr>
</div>
{% endfor %}
ファイルを保存して閉じます。
インデックスページに移動するか、更新します。
http://127.0.0.1:5000/
インデックスページの各投稿タイトルをクリックします。 これで、各投稿が適切な投稿ページにリンクしていることがわかります。
これで、個々の投稿を表示するためのページが作成されました。 次に、投稿ページにWebフォームを追加して、ユーザーが新しいコメントを追加できるようにします。
ステップ5—新しいコメントを追加する
このステップでは、 /<int:post_id>/
ルートとその post()
個々の投稿の表示を処理する表示機能。 各投稿の下にWebフォームを追加して、ユーザーがその投稿にコメントを追加できるようにします。次に、コメントの送信を処理してデータベースに追加します。
まず、 post.html
コメントの内容のテキスト領域とコメントの追加送信ボタンで構成されるWebフォームを追加するためのテンプレートファイル。
- nano templates/post.html
下にフォームを追加してファイルを編集します Comments
H3の見出し、およびその真上 for
ループ:
<hr>
<h3>Comments</h3>
<form method="post">
<p>
<textarea name="content"
placeholder="Comment"
cols="60"
rows="5"></textarea>
</p>
<p>
<button type="submit">Add comment</button>
</p>
</form>
{% for comment in post.comments %}
ファイルを保存して閉じます。
ここで、 <form>
属性を持つタグ method
に設定 post
フォームがPOSTリクエストを送信することを示します。
コメントのコンテンツ用のテキスト領域と送信ボタンがあります。
開発サーバーが実行されている状態で、ブラウザーを使用して投稿に移動します。
http://127.0.0.1:5000/2/
次のようなページが表示されます。
このフォームはPOSTリクエストをに送信します post()
ビュー機能ですが、フォームの送信を処理するコードがないため、フォームは現在機能しません。
次に、にコードを追加します post()
フォームの送信を処理し、データベースに新しいコメントを追加する関数を表示します。 開ける app.py
ユーザーが送信するPOSTリクエストを処理するには:
- nano app.py
編集します /<int:post_id>/
ルートとその post()
次のように見える関数を表示します。
@app.route('/<int:post_id>/', methods=('GET', 'POST'))
def post(post_id):
post = Post.query.get_or_404(post_id)
if request.method == 'POST':
comment = Comment(content=request.form['content'], post=post)
db.session.add(comment)
db.session.commit()
return redirect(url_for('post', post_id=post.id))
return render_template('post.html', post=post)
ファイルを保存して閉じます。
を使用してGETリクエストとPOSTリクエストの両方を許可します methods
パラメータ。 GETリクエストは、サーバーからデータを取得するために使用されます。 POSTリクエストは、特定のルートにデータを投稿するために使用されます。 デフォルトでは、GETリクエストのみが許可されます。
内部 if request.method == 'POST'
条件として、ユーザーがフォームを介して送信するPOSTリクエストを処理します。 を使用してコメントオブジェクトを作成します Comment
モデル、あなたがそこから抽出した提出されたコメントの内容をそれに渡す request.form
物体。 コメントが属する投稿を指定するには、 post
パラメータ、それを渡す post
投稿IDを使用して取得したオブジェクト。 get_or_404()
方法。
作成したコメントオブジェクトをデータベースセッションに追加し、トランザクションをコミットして、投稿ページにリダイレクトします。
次に、ブラウザの投稿ページを更新し、コメントを書き込んで送信します。 投稿の下に新しいコメントが表示されます。
これで、ユーザーが投稿にコメントを追加できるWebフォームができました。 Webフォームの詳細については、FlaskアプリケーションでWebフォームを使用する方法を参照してください。 Webフォームを管理するためのより高度で安全な方法については、Flask-WTFを使用してWebフォームを使用および検証する方法を参照してください。 次に、データベース内のすべてのコメントとそれらが投稿された投稿を表示するページを追加します。
ステップ6—すべてのコメントを表示する
このステップでは、コメントページを追加します。このページでは、データベース内のすべてのコメントを表示し、最新のコメントを最初に表示して順序付けます。 各コメントには、コメントが投稿された投稿のタイトルとリンクが含まれます。
開ける app.py
:
- nano app.py
ファイルの最後に次のルートを追加します。 これにより、データベース内のすべてのコメントが最新のものから順にフェッチされます。 次に、それらをというテンプレートファイルに渡します。 comments.html
、後で作成します。
# ...
@app.route('/comments/')
def comments():
comments = Comment.query.order_by(Comment.id.desc()).all()
return render_template('comments.html', comments=comments)
ファイルを保存して閉じます。
あなたは order_by()
上のメソッド query
特定の順序ですべてのコメントをフェッチする属性。 この場合、 desc()
上のメソッド Comment.id
コメントを降順でフェッチする列。最新のコメントが最初になります。 次に、 all()
結果を取得し、それをという変数に保存するメソッド comments
.
と呼ばれるテンプレートをレンダリングします comments.html
、それを渡す comments
最新のものから順に並べられたすべてのコメントを含むオブジェクト。
この新しいを開く comments.html
テンプレートファイル:
- nano templates/comments.html
その中に次のコードを入力します。 これにより、コメントとそれらが属する投稿へのリンクが表示されます。
{% extends 'base.html' %}
{% block content %}
<span class="title"><h1>{% block title %} Latest Comments {% endblock %}</h1></span>
<div class="content">
{% for comment in comments %}
<div class="comment">
<i>
(#{{ comment.id }})
<p>{{ comment.content }}</p>
</i>
<p class="title">
On <a href="{{ url_for('post',
post_id=comment.post.id) }}">
{{ comment.post.title }}
</a>
</p>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
ファイルを保存して閉じます。
ここでは、基本テンプレートを拡張し、タイトルを設定し、 for
ループ。 コメントのID、その内容、およびコメントが属する投稿へのリンクを表示します。 投稿データにアクセスするには、 comment.post
.
ブラウザを使用してコメントページに移動します。
http://127.0.0.1:5000/comments/
次のようなページが表示されます。
次に、 base.html
コメントナビゲーションバーリンクがこのコメントページを指すようにするためのテンプレート:
- nano templates/base.html
ナビゲーションバーを編集して、次のようにします。
<nav>
<a href="{{ url_for('index') }}">FlaskApp</a>
<a href="{{ url_for('comments') }}">Comments</a>
<a href="#">About</a>
</nav>
ファイルを保存して閉じます。
コメントページを更新すると、コメントナビゲーションバーリンクが機能することがわかります。
これで、データベース内のすべてのコメントを表示するページができました。 次に、投稿ページの各コメントの下にボタンを追加して、ユーザーがコメントを削除できるようにします。
ステップ7—コメントの削除
この手順では、各コメントの下にコメントの削除ボタンを追加して、ユーザーが不要なコメントを削除できるようにします。
まず、新しいものを追加します /comments/ID/delete
POSTリクエストを受け入れるルート。 ビュー関数は、削除するコメントのIDを受け取り、データベースからフェッチして削除し、削除されたコメントがあった投稿ページにリダイレクトします。
開ける app.py
:
- nano app.py
ファイルの最後に次のルートを追加します。
# ...
@app.post('/comments/<int:comment_id>/delete')
def delete_comment(comment_id):
comment = Comment.query.get_or_404(comment_id)
post_id = comment.post.id
db.session.delete(comment)
db.session.commit()
return redirect(url_for('post', post_id=post_id))
ファイルを保存して閉じます。
ここでは、通常の代わりに app.route
デコレータでは、Flaskバージョン2.0.0で導入されたapp.post デコレータを使用します。これにより、一般的なHTTPメソッドのショートカットが追加されました。 例えば、 @app.post("/login")
のショートカットです @app.route("/login", methods=["POST"])
. これは、このビュー関数がPOSTリクエストのみを受け入れ、 /comments/ID/delete
ブラウザのルートは 405 Method Not Allowed
エラー。WebブラウザはデフォルトでGETリクエストを使用しているためです。 コメントを削除するには、ユーザーはこのルートにPOSTリクエストを送信するボタンをクリックします。
これ delete_comment()
ビュー機能は、削除するコメントのIDを受信します。 comment_id
URL変数。 あなたは get_or_404()
コメントを取得して保存する方法 comment
変数、またはで応答する 404 Not Found
コメントが存在しない場合。 コメントが属する投稿の投稿IDをに保存します post_id
コメントを削除した後に投稿にリダイレクトするために使用する変数。
あなたは delete()
行のデータベースセッションのメソッド db.session.delete(comment)
、コメントオブジェクトを渡します。 これにより、トランザクションがコミットされるたびにコメントを削除するようにセッションが設定されます。 他の変更を行う必要がないため、を使用してトランザクションを直接コミットします。 db.session.commit()
. 最後に、削除されたコメントが投稿された投稿にユーザーをリダイレクトします。
次に、 post.html
各コメントの下にコメントの削除ボタンを追加するためのテンプレート:
- nano templates/post.html
編集します for
新しいを追加してループする <form>
コメントコンテンツのすぐ下にタグを付けます。
{% for comment in post.comments %}
<div class="comment">
<p>#{{ comment.id }}</p>
<p>{{ comment.content }}</p>
<form method="POST"
action="{{ url_for('delete_comment',
comment_id=comment.id) }}">
<input type="submit" value="Delete Comment"
onclick="return confirm('Are you sure you want to delete this entry?')">
</form>
</div>
{% endfor %}
ファイルを保存して閉じます。
ここに、POSTリクエストを送信するWebフォームがあります。 delete_comment()
ビュー機能。 あなたは合格します comment.id
の引数として comment_id
削除されるコメントを指定するパラメーター。 Webブラウザで利用可能なconfirm()メソッド関数を使用して、リクエストを送信する前に確認メッセージを表示します。
次に、ブラウザの投稿ページに移動します。
http://127.0.0.1:5000/2/
各コメントの下にコメントの削除ボタンが表示されます。 それをクリックして、削除を確認します。 コメントが削除されたことがわかります。
これで、データベースからコメントを削除する方法があります。
結論
Flask-SQLAlchemy拡張機能を使用して1対多の関係を管理する方法を示す小さなブログシステムを構築しました。 親テーブルを子テーブルに接続する方法、子オブジェクトをその親に関連付けてデータベースに追加する方法、および親エントリから子データにアクセスする方法、およびその逆の方法を学習しました。
Flaskの詳細については、Flaskを使用してWebアプリケーションを構築する方法シリーズの他のチュートリアルをご覧ください。