Re:ゼロから始める文系プログラマ

未経験がプログラミングを通して人生を変える

【Flask】電子掲示板を作る~⑥投稿内容の詳細・削除ページを実装する~


スポンサードリンク
 

f:id:ShotaNukumizu_1000:20210718062603p:plain

おはようございます。Shotaです。

今日は一昨日に引き続き、PythonのFlaskを用いた電子掲示板の作り方について解説していきます。

今日の記事では投稿を作るページを機能させる方法を徹底解説します。

昨日の記事では、投稿作成ページを機能させる方法を説明しました。使用したソースコードを以下に載せておきます。

main.py

from flask import Flask, render_template, request, redirect
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime, date

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///postdata.db'
db = SQLAlchemy(app)

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user = db.Column(db.String(20), nullable=False)
    title = db.Column(db.String(30), nullable=False)
    detail = db.Column(db.Text, nullable=False)
    post_date = db.Column(db.DateTime, nullable=False)

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'GET':
        posts = Post.query.all()
        return render_template('index.html', posts=posts)
    else:
        user = request.form.get('user')
        title = request.form.get('title')
        detail = request.form.get('detail')
        post_date = request.form.get('post_date')

        post_date = datetime.strptime(post_date, '%Y-%m-%d')
        new_post = Post(user=user, title=title, detail=detail, post_date=post_date)

        db.session.add(new_post)
        db.session.commit()
        return redirect('/')


@app.route('/create')
def create():
    return render_template('create.html')

if __name__ == '__main__':
    app.run(debug=True)

index.html

{% extends 'base.html' %}

{% block body %}
<h1>Index Page</h1>

<a href="/create" class="btn-lg btn-info" role="button">投稿する</a>

<div class="container">
  {% for post in posts %}
  <div class="card">
      <h5 class="card-header">日付:{{ post.post_date.date() }}</h5>
      <div class="card-body">
        <h5 class="card-title">タイトル:{{ post.title }}</h5>
        <p class="card-text">投稿者:{{ post.user }}</p>
        <a role="button" href="/detail" class="btn-lg btn-secondary">詳細</a>
        <a role="button" href="/delete" class="btn-lg btn-danger">削除</a>
      </div>
  </div>
  {% endfor %}
</div>
{% endblock %}

create.html

{% extends 'base.html' %}

{% block body %}
<h1>新規作成</h1>

<div class="container">
    <form action="/" method="POST">
        <p>日付:<input type="date" name="post_date"></p>
        <input class="form-control" name="user" type="text" placeholder="名前(全角20文字以内)" aria-label="default input example">
        <input class="form-control" name="title" type="text" placeholder="タイトル(全角30文字以内)" aria-label="default input example">
        <div class="mb-3">
            <label for="exampleFormControlTextarea1" class="form-label">投稿内容</label>
            <textarea class="form-control"  name="detail" id="exampleFormControlTextarea1" rows="3"></textarea>
        </div>
        <a href="/" class="btn btn-outline-success">戻る</a>
        <button type="submit" class="btn btn-success">投稿する</button>
    </form> 
</div>

{% endblock %}



投稿内容の詳細ページを実装する

投稿した内容の詳細を表示させるためには、Pythonとhtmlの両方を編集する必要があります。

まずは、Python側からコードを追加していきましょう。


Pythonファイル

投稿した内容の詳細を表示するためにPythonでやることは、次のコード追加だけです。

@app.route('/detail/<int:id>')
def detail(id):
    post = Post.query.get(id)
    return render_template('detail.html', post=post)

全体を確認しておくと、次のようになっています。

from flask import Flask, render_template, request, redirect
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime, date

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///postdata.db'
db = SQLAlchemy(app)

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user = db.Column(db.String(20), nullable=False)
    title = db.Column(db.String(30), nullable=False)
    detail = db.Column(db.Text, nullable=False)
    post_date = db.Column(db.DateTime, nullable=False)

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'GET':
        posts = Post.query.all()
        return render_template('index.html', posts=posts)
    else:
        user = request.form.get('user')
        title = request.form.get('title')
        detail = request.form.get('detail')
        post_date = request.form.get('post_date')

        post_date = datetime.strptime(post_date, '%Y-%m-%d')
        new_post = Post(user=user, title=title, detail=detail, post_date=post_date)

        db.session.add(new_post)
        db.session.commit()
        return redirect('/')


@app.route('/create')
def create():
    return render_template('create.html')

@app.route('/detail/<int:id>')
def detail(id):
    post = Post.query.get(id)
    return render_template('detail.html', post=post)

if __name__ == '__main__':
    app.run(debug=True)

詳細ページにアクセスするためには、「どの投稿の詳細ページを開くのか」が重要になっていきます。そのため、URLは/detailだけではなく、/detail/<int:id>と指定しましょう。

idはデータベースを作成する時に定義しました。作成したフォームにはidが指定されていませんが、データベースにタスクを登録する際に、自動的に番号を割り振られます。

post = Post.query.get(id)

これを使うことで、該当するidの投稿内容を取得し、そのタスクをdetail.htmlに渡している、ということになります。

あとはこの機能をhtmlに付け加えるだけです。


htmlファイル

まずは、新しくdetail.htmlのファイルを作成しましょう。ファイルを作成したら、Bootstrapにアクセスして「card」と検索してください。

検索したら、以下の画像のコードを使います。

f:id:ShotaNukumizu_1000:20210825093330p:plain


▼使用するコード

<div class="card border-dark mb-3" style="max-width: 18rem;">
     <div class="card-header">Header</div>
     <div class="card-body text-dark">
        <h5 class="card-title">Dark card title</h5>
        <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
     </div>
</div>

このコードに多少のアレンジを加えていきます。

<div class="container">
    <div class="card border-dark mb-3" style="max-width: 30rem;">
        <div class="card-header">日付:{{ post.post_date.date() }}、投稿者:{{ post.user }}</div>
        <div class="card-body text-dark">
          <h5 class="card-title">タイトル:{{ post.title }}</h5>
          <p class="card-text">{{ post.detail }}</p>
        </div>
    </div>
</div>

あとは、index.htmlの編集も少しやってみましょう。

理由は、各投稿ごとに詳細ページへのリンクを設置したいからです。

以下のように、詳細ページへアクセスできるURLを設置してあげましょう。

<a role="button" href="/detail/{{ post.id }}" class="btn-lg btn-secondary">詳細</a>

index.htmlファイルの全体像

{% extends 'base.html' %}

{% block body %}
<h1>Index Page</h1>

<a href="/create" class="btn-lg btn-info" role="button">投稿する</a>

<div class="container">
  {% for post in posts %}
  <div class="card">
      <h5 class="card-header">日付:{{ post.post_date.date() }}</h5>
      <div class="card-body">
        <h5 class="card-title">タイトル:{{ post.title }}</h5>
        <p class="card-text">投稿者:{{ post.user }}</p>
        <a role="button" href="/detail/{{ post.id }}" class="btn-lg btn-secondary">詳細</a>
        <a role="button" href="/delete" class="btn-lg btn-danger">削除</a>
      </div>
  </div>
  {% endfor %}
</div>
{% endblock %}

あとは、Pythonファイルを実行してサーバを立ち上げ、詳細ページにアクセスして確認してみましょう。

以下のようなページにアクセスすることができます。

f:id:ShotaNukumizu_1000:20210825094932p:plain

あとは、トップページに戻るためのボタンを設置します。ボタンは以下の画像のものを使います。

f:id:ShotaNukumizu_1000:20210825095303p:plain

次のコードをコピペしてindex.htmlに貼り付けてください。

<a href="/" type="button" class="btn btn-secondary btn-lg">戻る</a>

index.htmlの全体像

{% extends 'base.html' %}

{% block body %}

<h1>詳細ページ</h1>

<div class="container">
    <div class="card border-dark mb-3" style="max-width: 30rem;">
        <div class="card-header">日付:{{ post.post_date.date() }}、投稿者:{{ post.user }}</div>
        <div class="card-body text-dark">
          <h5 class="card-title">タイトル:{{ post.title }}</h5>
          <p class="card-text">{{ post.detail }}</p>
        </div>
    </div>
    <a href="/" type="button" class="btn btn-secondary btn-lg">戻る</a>
</div>
{% endblock %}

再度サーバを立ち上げると、次のような画像が表示されます。これでトップページに戻る機能を追加できました。

f:id:ShotaNukumizu_1000:20210825095654p:plain


削除ページの実装

次は、投稿内容を削除するページを実装していきます。タスクの削除については、新しいページを追加せずにトップページからできるようにすればいいですね。


Pythonファイル

main.pyに以下のコードを追加していきましょう。

@app.route('/delete/<int:id>')
def delete(id):
    post = Post.query.get(id)

    db.session.delete(post)
    db.session.commit()
    return redirect('/')

投稿を追加するときは、commit()する前にaddを行っていました。

今回は投稿を削除するので、delete()を使うということは直感的にも理解できると思います。

データベースの処理が終わったら、トップページへリダイレクトします。


htmlファイル

htmlファイルの操作は非常に簡単です。

index.htmlで、次のコードに編集するだけです。

<a role="button" href="/delete/{{ post.id }}" class="btn-lg btn-danger">削除</a>

index.htmlの全体像

{% extends 'base.html' %}

{% block body %}
<h1>Index Page</h1>

<a href="/create" class="btn-lg btn-info" role="button">投稿する</a>

<div class="container">
  {% for post in posts %}
  <div class="card">
      <h5 class="card-header">日付:{{ post.post_date.date() }}</h5>
      <div class="card-body">
        <h5 class="card-title">タイトル:{{ post.title }}</h5>
        <p class="card-text">投稿者:{{ post.user }}</p>
        <a role="button" href="/detail/{{ post.id }}" class="btn-lg btn-secondary">詳細</a>
        <a role="button" href="/delete/{{ post.id }}" class="btn-lg btn-danger">削除</a>
      </div>
  </div>
  {% endfor %}
</div>
{% endblock %}

これでサーバを立ち上げて「削除」ボタンを押すと、投稿内容が消えていることがわかります。これで実装は終了です。


まとめ

今日の記事では投稿内容の詳細・削除ページを実装する方法について詳細に書きました。

今回作ったアプリは見た目が整っていない部分も十分にありますので、各自ネット記事を参考にして見た目を整えておいてください。

アプリのデプロイ方法は後日詳細に話していきます。

先週から6回に渡ってFlaskアプリの作り方を簡単に解説しましたが、この記事を参考にFlaskとPythonに対する理解が深まると幸いです。

長くなりましたが、今日の記事はこれで終了です。


開発環境

【参考サイト】

tech-diary.net