Chat GPTを使って簡単に掲示板アプリを作ってみた

App
AIイメージ

最近巷で詳細な応答を行うように訓練された「Chat GPT」が話題となっています。これにより、質問に端的に答えてくれたり、フォローアップを行ったり、ソースコードのチェックなんかもしてくれるため、人間の面倒な作業の時間短縮が期待されます。

曖昧な質問やコメントに対しても、意図を汲み取って回答してくれるため、完璧ではないにしろおおよそ「すごい・・・勝てない・・・」「これからの人の役割って・・・」と考えさせられる代物です。詳細はここでは書かないので、公式を確認してみてください。

Chat GPTにはGPT3という言語モデルが使われており、実は2020年には公開されていたようですが、そのチャット応答のテスト版がこの度公開されたという経緯です。

今回は、この「Chat GPT」を使って、pythonのwebフレームワークのFlaskを使って掲示板アプリを作ってみます。

要件定義

簡単な機能をリストで書いて、概要を伝達

人

flaskで簡単な掲示板アプリを作成します。 まず、簡単に機能の整理をします。
・ログイン機能はなし
・投稿に関する情報は、id, 投稿文、投稿時間、投稿主名
・投稿に対して、コメントをつけられる
・投稿、コメントに編集機能、削除機能はなし

要件定義の補足をしてください。

chat GPT
chat GPT

この掲示板アプリでは、投稿を行う際に投稿主の名前を入力するようにします。また、投稿された内容は投稿時間を含めた情報とともに表示されます。投稿に対しては、その投稿に対してコメントを投稿することができます。また、このアプリでは投稿やコメントの編集や削除機能は実装されていません。

リストで書いたことをそのまま文章にされた感じですね。意図と違ったことは書かれていないので、このまま進めます。

アプリの基本であるDBを設計

人

DBはsqliteを採用し、sql_alchemyとflask-migrateも使用します。
これで、テーブル設計をしてください。

chat GPT
chat GPT

この掲示板アプリでは、投稿された内容や情報を保存するために、次のようなテーブルを作成することをおすすめします。

  • posts テーブル: このテーブルは、投稿された内容や投稿者の情報を保存するためのものです。
    • id: 投稿のID。これは投稿ごとに一意である必要があります。
    • name: 投稿者の名前。
    • text: 投稿された内容。
    • time: 投稿された時間。
  • comments テーブル: このテーブルは、投稿に対するコメントを保存するためのものです。
    • id: コメントのID。これはコメントごとに一意である必要があります。
    • post_id: コメントが対応する投稿のID。これを使用することで、投稿とコメントを紐付けることができます。
    • name: コメントを投稿した者の名前。
    • text: コメントの内容。
    • time: コメントが投稿された時間。

上記のようなテーブルを作成することで、投稿された内容や情報を保存し、必要なときに参照することができるようになります。

ちゃんと、リレーションまで組んでくれていますね。素晴らしい。

コーディング

モデル設計に基づいたmodels.pyの作成

人

テーブル設計に基づいたmodels.pyを作成します。

chat GPT
chat GPT

次のような models.py ファイルを作成します。

# Flask-SQLAlchemyをインポート
from flask_sqlalchemy import SQLAlchemy

# SQLAlchemyのインスタンスを作成
db = SQLAlchemy()

# 投稿を表すクラス
class Post(db.Model):
    # 投稿のID
    id = db.Column(db.Integer, primary_key=True)
    # 投稿者の名前
    name = db.Column(db.String(20), nullable=False)
    # 投稿された内容
    text = db.Column(db.String(200), nullable=False)
    # 投稿された時間
    time = db.Column(db.DateTime, nullable=False)

# コメントを表すクラス
class Comment(db.Model):
    # コメントのID
    id = db.Column(db.Integer, primary_key=True)
    # コメントが対応する投稿のID
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
    # コメントを投稿した者の名前
    name = db.Column(db.String(20), nullable=False)
    # コメントの内容
    text = db.Column(db.String(200), nullable=False)
    # コメントが投稿された時間
    time = db.Column(db.DateTime, nullable=False)
chat GPT
chat GPT

このようにすることで、投稿やコメントの情報を保存するためのデータモデルを定義することができます。

また、Post クラスと Comment クラスは、SQLAlchemy の db.Model クラスを継承しています。これにより、Flask-SQLAlchemy が提供する ORマッピング機能を使用して、データベースとのやりとりを行うことができます。

さらに、Post クラスの timeComment クラスの time は、それぞれ日時を表す db.DateTime 型を指定しています。これにより、投稿やコメントが投稿された時間を保存することができます。

migration管理のためにapp.pyを修正

人

こちらのモデルをflask-migrateを使ってmigrateします。

chat GPT
chat GPT

app.py ファイルを次のように更新します。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

# Flaskアプリケーションのインスタンスを作成
app = Flask(__name__)

# FlaskアプリケーションとSQLAlchemyを紐付ける
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'
db = SQLAlchemy(app)

# Flask-Migrateをインスタンス化する
migrate = Migrate(app, db)

# Flaskアプリケーションのエントリポイント
if __name__ == '__main__':
    app.run()
chat GPT
chat GPT

このようにすることで、FlaskアプリケーションとSQLAlchemyを紐付けることができます。また、Flask-Migrateをインスタンス化することで、データベースのマイグレーションを管理することができます。

次に、models.py ファイルを次のように更新します。

migrate用にapp.pyを書き換えてくれました。models.pyをインポートしてなかったりしますが、こちらで修正してあげましょう。flask-migrationを使ってアプリ内にsqliteのDBを作成して、こちらで管理していきます。

投稿機能を作成

人

トップページには、投稿一覧を表示します。
また、投稿作成ボタンも設置します。
まず、app.pyを作成します。

chat GPT
chat GPT

次のような app.py ファイルを作成します。

from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

# Flaskアプリケーションのインスタンスを作成
app = Flask(__name__)

# FlaskアプリケーションとSQLAlchemyを紐付ける
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'
db = SQLAlchemy(app)

# Flask-Migrateをインスタンス化する
migrate = Migrate(app, db)

# 投稿を表すクラス
class Post(db.Model):
    # 投稿のID
    id = db.Column(db.Integer, primary_key=True)
    # 投稿者の名前
    name = db.Column(db.String(20), nullable=False)
    # 投稿された内容
    text = db.Column(db.String(200), nullable=False)
    # 投稿された時間
    time = db.Column(db.DateTime, nullable=False)

# コメントを表すクラス
class Comment(db.Model):
    # コメントのID
    id = db.Column(db.

モデルは外部に書き出したはずなのですが、app.pyの中に戻ってきてしまっています。こちらを改めて指示します。

人

モデルは外部ファイルのmodels.pyに書き出します。

chat GPT
chat GPT

次のように app.py ファイルを更新します。

from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from models import db, Post, Comment

# Flaskアプリケーションのインスタンスを作成
app = Flask(__name__)

# FlaskアプリケーションとSQLAlchemyを紐付ける
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'
db.init_app(app)

# Flask-Migrateをインスタンス化する
migrate = Migrate(app, db)

# トップページ用のルーティング
@app.route('/')
def index():
    # 投稿を全て取得する
    posts = Post.query.all()
    return render_template('index.html', posts=posts)

# Flaskアプリケーションのエントリポイン

途切れてしまいました。ですが、トップページまではできていますね。こちらで、htmlファイルまで作ってもらいましょう。

人

index.htmlを作って下さい。

chat GPT
chat GPT

次のように index.html ファイルを作成します。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>掲示板</title>
</head>
<body>
    <h1>掲示板</h1>

    {% for post in posts %}
    <h2>{{ post.name }}</h2>
    <p>{{ post.text }}</p>
    <p>{{ post.time }}</p>
    {% endfor %}

    <h2>新規投稿</h2>
    <form action="/create" method="post">
        <label for="name">投稿者名:</label>
        <input type="text" id="name" name="name"><br>
        <label for="text">投稿内容:</label>
        <textarea id="text" name="text"></textarea><br>
        <input type="submit" value="投稿する">
    </form>
</body>
</html>

指示はしていませんが、投稿画面の一覧の下部に新規投稿フォームも盛り込んだ仕様になっています。また、cssは効いていないものの、Jinjaを使ってapp.pyのデータを出力してくれています。

人

投稿作成画面のルーティングを作って下さい。

chat GPT
chat GPT

次に、投稿作成画面のルーティングを実装します。

投稿作成画面では、新しい投稿をデータベースに保存するためのロジックが実装されます。

まず、 app.py ファイルを次のように更新します。

from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from models import Post, Comment, db
import datetime

app = Flask(__name__)
# FlaskアプリケーションとSQLAlchemyを紐付ける
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'

db.init_app(app)
# Flask-Migrateをインスタンス化する
migrate = Migrate(app, db)

# トップページ用のルーティング
@app.route('/')
def index():
    # 投稿を全て取得する
    posts = Post.query.all()
    return render_template('index.html', posts=posts)

# 投稿作成用のルーティング
@app.route('/create', methods=['POST'])
def create():
    # フォームから送信された情報を取得
    name = request.form['name']
    text = request.form['text']
    # 現在時刻を取得
    now = datetime.datetime.now()
    # 新しい投稿を作成
    post = Post(name=name, text=text, time=now)
    # 投稿をデータベースに保存
    db.session.add(post)
    db.session.commit()
    # トップページにリダイレクト
    return redirect('/')

# Flaskアプリケーションのエントリポイント
if __name__ == '__main__':
    app.run(debug=True)

いいですね。createルーティングが追加されました。トップページにリダイレクトするのもいい感じです。

味気ないので、cssを適用

人

style.cssを書きます。

chat GPT
chat GPT

style.cssファイルは次のように記述することができます。

body {
  font-family: sans-serif;
  color: #333;
}

.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

.post {
  margin-bottom: 20px;
  padding: 20px;
  background-color: #f5f5f5;
  border-radius: 5px;
}

.post-header {
  margin-bottom: 10px;
}

.post-header .name {
  font-weight: bold;
  margin-right: 10px;
}

.post-header .time {
  font-size: 12px;
  color: #999;
}

.post-body {
  white-space: pre-wrap;
}

form {
  margin-bottom: 20px;
}

label {
  display: block;
  margin-bottom: 8px;
}

input[type="text"], textarea {
  width: 100%;
  padding: 12px 20px;
  margin: 8px 0;
  box-sizing: border-box;
  border: 1px solid #ccc;
  border-radius: 4px;
}

input[type="submit"] {
  width: 100%;
  background-color: #4CAF50;
  color: white;
  padding: 14px 20px;
  margin: 8px 0;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

input[type="submit"]:hover {
  background-color: #45a049;
}

色とかセンスは不明ですが、hoverとかおまけも書いてくれています。ありがとう。

人

index.htmlを修正します。
style.cssのcssを当てるため、クラスやidを追加します。

chat GPT
chat GPT

index.htmlファイルは次のように記述することができます。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>掲示板アプリ</title>
  <link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
  <div class="container">
    <h1>掲示板</h1>

    {% for post in posts %}
    <div class="post">
      <div class="post-header">
        <span class="name">{{ post.name }}</span>
        <span class="time">{{ post.time }}</span>
      </div>
      <div class="post-body">
        {{ post.text }}
      </div>
    </div>
    {% endfor %}

    <h2>新規投稿</h2>
    <form action="/create" method="post">
        <label for="name">投稿者名:</label>
        <input type="text" id="name" name="name"><br>
        <label for="text">投稿内容:</label>
        <textarea id="text" name="text"></textarea><br>
        <input type="submit" value="投稿する">
    </form>
  </div>
</body>
</html>

コメント機能の実装

人

コメントのルーティングを作成します。
以下を追加します。 @app.route(‘/post/{id}’, methods=[‘GET’]) 

chat GPT
chat GPT

次のように app.py ファイルを更新します。

from flask import Flask, render_template, request, redirect
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from models import Post, Comment, db
import datetime

app = Flask(__name__)
# FlaskアプリケーションとSQLAlchemyを紐付ける
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'

db.init_app(app)
# Flask-Migrateをインスタンス化する
migrate = Migrate(app, db)

# トップページ用のルーティング
@app.route('/')
def index():
    # 投稿を全て取得する
    posts = Post.query.all()
    return render_template('index.html', posts=posts)

# 投稿詳細用のルーティング
@app.route('/post/<int:id>', methods=['GET'])
def post_detail(id):
    # 投稿を取得
    post = Post.query.get(id)
    # コメントを取得
    comments = Comment.query.filter_by(post_id=id).all()
    return render_template('post_detail.html', post=post, comments=comments)

# 投稿作成用のルーティング
@app.route('/create',

モデル定義が引き継がれているからでしょうか。投稿詳細画面で、コメント一覧も取得、レスポンスしてくれています。

人

投稿詳細用のhtml, post.htmlを作成します。

chat GPT
chat GPT

次のように post_detail.html ファイルを記述することができます。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>投稿詳細 - 掲示板アプリ</title>
  <link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
  <div class="container">
    <div class="post">
      <div class="post-header">
        <span class="name">{{ post.name }}</span>
        <span class="time">{{ post.time }}</span>
      </div>
      <div class="post-body">
        {{ post.text }}
      </div>
    </div>

    <h2>コメント一覧</h2>
    {% for comment in comments %}
    <div class="comment">
      <div class="comment-header">
        <span class="name">{{ comment.name }}</span>
        <span class="time">{{ comment.time }}</span>
      </div>
      <div class="comment-body">
        {{ comment.text }}
      </div>
    </div>
    {% endfor %}

    <h2>新規コメント</h2>
    <form action="/create_comment" method="post">
        <input type="hidden" name="post_id" value="{{ post.id }}">
        <label for="name">投稿者名:</label>
        <input type="text" id="name" name="name"><br>
        <label for="text">投稿内容:</label>
        <textarea id="text" name="text"></textarea><br>
        <input type="submit" value="投稿する">
    </form>
  </div>
</body>
</html>

投稿一覧画面に詳細画面につながるリンクを追加

人

index.htmlのdiv postに’/post/<int:id>’につながるリンクボタンを作成する。

chat GPT
chat GPT

次のように index.html ファイルを更新します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>掲示板アプリ</title>
  <link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
  <div class="container">
    <h1>掲示板</h1>

    {% for post in posts %}
    <div class="post">
      <div class="post-header">
        <span class="name">{{ post.name }}</span>
        <span class="time">{{ post.time }}</span>
        <a href="/post/{{ post.id }}" class="post-link">詳細</a>
      </div>
      <div class="post-body">
        {{ post.text }}
      </div>
    </div>
    {% endfor %}

    <h2>新規投稿</h2>
    <form action="/create" method="post">
        <label for="name">投稿者名:</label>
        <input type="text" id="name" name="name"><br>
        <label for="text">投稿内容:</label>
        <textarea id="text" name="text"></textarea><br>
        <input type="submit" value="投稿する">
    </form>
  </div>
</body>
</html>

コメント作成用のルーティング追加

人

@app.route(‘/post/<int:id>/create’) に続くコードを作成します。

chat GPT
chat GPT

次のように app.py ファイルを更新します。

from flask import Flask, render_template, request, redirect
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from models import Post, Comment, db
import datetime

app = Flask(__name__)
# FlaskアプリケーションとSQLAlchemyを紐付ける
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'

db.init_app(app)
# Flask-Migrateをインスタンス化する
migrate = Migrate(app, db)

# トップページ用のルーティング
@app.route('/')
def index():
    # 投稿を全て取得する
    posts = Post.query.all()
    return render_template('index.html', posts=posts)

# 投稿詳細用のルーティング
@app.route('/post/<int:id>', methods=['GET'])
def post_detail(id):
    # 投稿を取得
    post = Post.query.get(id)
    # コメントを取得
    comments = Comment.query.filter_by(post_id=id).all()
    return render_template('post_detail.html', post=post, comments=comments)

# コメント作成用のルーティング
@app.route('/post/<int:id>/create', methods=['POST'])
def create_comment(id):
    # フォームから送信された情報を取得
    name = request.form['name']
    text = request.form['text']
    # 現在時刻を取得
    now = datetime.datetime.now()
    # 新しいコメントを作成
    comment = Comment(name=name, text=text, time=now, post_id=id)
    # コメントを追加
    db.session.add(comment)
    # コメントを保存
    db.session.commit()
    return redirect(f'/post/{id}')

# Flaskアプリケーションのエントリポイント
if __name__ == '__main__':
    app.run(debug=True)

完成画面

まとめ

素晴らしいのひとこと

コードは書けないとか言ってくることもありますが、人間目には書いてくれているように見えます。細かい指示を出せば出すほど学習された文字列から近しいものを出力してくれるのでしょう。コード部分に関しては大部分をお任せしているような状態でした。

一方では課題もあり

実は、何度かリテイクしていたりします。それに、「彼」自信が発言しているように、あくまで自然言語処理での応答をしているにすぎず、コードを理解しているわけではないようで、所々動かない部分もあります。

また、前提知識を埋め込んでいく必要があるので、使用用途はこの程度の小規模アプリだったり、機能の一部分だけを拾ってくることに限定されます。(それでもすごく助かります!)

ソースコードが長すぎると出力結果が途切れてしまったりもします。サーバー負荷の関係から現在は制限を設けているのかな?と思います。ここら辺もそのうち解消されてくるのでしょうが。

これからのプログラミング

このツールの存在は、正直、楽しみでもあり、怖くもありという感想です。「プログラムを書く」という作業自体は減っていくだろうと思います。

限りなく完璧な詳細設計ができる人がいれば、大部分をAIに任せていき、プログラムを書く人員は削減されていくのでしょう。ただし、設計ができればサービスがうまくいくというわけでもなく、ビジネス面、設計、デザイン、分析等をして、より良いものを作っていくための仕様の策定、指示をしていく必要があります。人間が強みとするところはどこか、AI以上に付加価値を出せるところはどこなのか、そんなスキルを身につけていけなければなりませんね。

参考

コメント

タイトルとURLをコピーしました