Skip to content
Code Blue プログラミング学習ノート
Python/Flask

Flaskアプリ開発実践

StudyLog:学習ログ共有アプリ

学習ログの一覧表示や詳細表示、投稿・編集・削除ができる学習記録アプリを作成する実践です。ユーザー登録・ログイン・ログアウト機能を備え、一般ユーザーは自分が投稿した学習ログのみ編集・削除でき、管理者はすべての投稿を管理できます。また、タグ、コメント、いいね機能を実装し、ユーザー同士が交流しながら学習を進められるようにしています。

さらに、画像アップロード機能やサムネイル生成機能を備え、投稿内容を見やすく表示できます。REST APIを利用して投稿データを取得するとともに、外部APIからGitHubリポジトリ情報などを取得し、学習成果を充実して表示します。また、適切なエラーハンドリングやテストを実施し、セキュリティ対策を講じることで、安全性と信頼性の高いシステムを実現しています。

1. Flaskルーティングとビュー関数

演習テーマ

StudyLogの基本ページを作る

演習問題

Flaskで学習ログアプリの土台を作成し、以下のページをルーティングで表示できるようにしてください。

実装するページ

URL内容
/トップページ
/logs学習ログ一覧ページ
/logs/<int:log_id>学習ログ詳細ページ
/aboutアプリ説明ページ

要件

  • app.pyを作成する
  • 各URLに対応するビュー関数を作る
  • /logs/<int:log_id> では、URLの数値を画面に表示する
  • 存在しないログIDの場合は「ログが見つかりません」と表示する

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── app.py
└── application/
    ├── __init__.py
    └── routes/
        ├── __init__.py
        └── main.py
```

### 追加される主な役割

- `app.py`:Flaskアプリを起動する入口
- `application/__init__.py`:Flaskアプリを作成する場所
- `application/routes/main.py`:トップ、一覧、詳細、説明ページのルーティングを定義

完成品質ゴール

  • トップページ、学習ログ一覧ページ、学習ログ詳細ページ、アプリ説明ページに正しくアクセスできる。
  • URLごとに適切なビュー関数が実行され、意図した内容が画面に表示される。
  • /logs/<int:log_id> のような動的URLを使い、URLから受け取ったIDに応じて表示内容を切り替えられる。
  • 存在しない学習ログIDにアクセスした場合、アプリが停止せず「ログが見つかりません」と表示できる。
  • ルーティングの記述が整理されており、後からページを追加しやすい構成になっている。

2. FlaskテンプレートエンジンJinja2の基礎

演習テーマ

HTMLテンプレートで学習ログを表示する

演習問題

前回作成したStudyLogにJinja2テンプレートを導入し、学習ログ一覧と詳細ページをHTMLで表示してください。

実装する内容

  • templatesフォルダを作成する
  • base.htmlを作成する
  • index.html
  • logs.html
  • log_detail.html
  • about.html

要件

  • base.htmlに共通レイアウトを作る
  • {% block content %} を使う
  • 学習ログの仮データをPythonのリストで用意する
  • {% for %} を使ってログ一覧を表示する
  • {% if %} を使ってログがない場合の表示を切り替える
  • 詳細ページではタイトル、本文、カテゴリ、学習時間を表示する

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── app.py
└── application/
    ├── __init__.py
    ├── routes/
    │   ├── __init__.py
    │   └── main.py
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        └── logs/
            ├── list.html
            └── detail.html
```

### 追加される主な役割

- `templates/base.html`:共通レイアウト
- `templates/index.html`:トップページ
- `templates/about.html`:アプリ説明ページ
- `templates/logs/list.html`:学習ログ一覧ページ
- `templates/logs/detail.html`:学習ログ詳細ページ

仮データ例

logs = [
    {
        "id": 1,
        "title": "Flaskのルーティングを学習",
        "body": "URLとビュー関数の関係を理解した。",
        "category": "Flask",
        "study_time": 60
    }
]

完成品質ゴール

  • Python側で用意した学習ログのデータを、HTMLテンプレート上に正しく表示できる。
  • base.html を使った共通レイアウトが作られており、各ページで同じヘッダーやナビゲーションが表示される。
  • 学習ログ一覧ページでは、複数のログがカードやリスト形式で繰り返し表示される。
  • 学習ログが存在しない場合は、「まだ学習ログがありません」などの適切なメッセージが表示される。
  • 詳細ページでは、選択した学習ログのタイトル、本文、カテゴリ、学習時間などが正しく表示される。
  • HTMLとPythonの処理が分離され、見た目の変更をテンプレート側で管理できる状態になっている。

3. Flaskでの静的ファイルの取り扱い

演習テーマ

StudyLogの見た目を整える

演習問題

CSSや画像を使って、StudyLogの画面デザインを整えてください。

実装する内容

  • static/css/style.css を作成する
  • static/images/ にロゴ画像やサンプル画像を配置する
  • base.htmlからCSSを読み込む
  • ナビゲーションバーを作る
  • 学習ログ一覧をカード型デザインにする

要件

  • url_for(‘static’, filename=’css/style.css’) を使ってCSSを読み込む
  • トップページにアプリのロゴを表示する
  • 学習ログ一覧をカード形式で表示する
  • カテゴリごとに見た目を少し変える
  • スマホでも見やすい簡単なレスポンシブ対応を入れる

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── app.py
└── application/
    ├── __init__.py
    ├── routes/
    │   ├── __init__.py
    │   └── main.py
    ├── static/
    │   ├── css/
    │   │   └── style.css
    │   └── images/
    │       └── default.svg
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        └── logs/
            ├── list.html
            └── detail.html
```

### 追加される主な役割

- `static/css/style.css`:全体デザイン
- `static/images/default.svg`:トップページや画像未登録時に使う仮画像

完成品質ゴール

  • CSSファイルが正しく読み込まれ、全ページに共通のデザインが適用されている。
  • トップページにロゴ画像やメインビジュアルが表示され、アプリらしい見た目になっている。
  • 学習ログ一覧がカード型デザインになっており、タイトル・カテゴリ・学習時間などが見やすく整理されている。
  • ナビゲーションバーがあり、主要ページへ移動しやすい。
  • url_for(‘static’, filename=’…’) を使ってCSSや画像を安全に読み込んでいる。
  • PC・スマートフォンの両方で極端に崩れないレイアウトになっている。

4. Flaskでのフォーム処理の基礎

演習テーマ

学習ログ投稿フォームを作る

演習問題

ユーザーが学習ログを投稿できるフォームを作成してください。

実装するページ

URL内容
/logs/new新規投稿フォーム
/logs投稿後の一覧表示

フォーム項目

  • タイトル
  • 本文
  • カテゴリ
  • 学習時間
  • 学習日

要件

  • GETリクエストでフォームを表示する
  • POSTリクエストで入力内容を受け取る
  • 入力されたログを一覧に追加する
  • タイトルが空の場合はエラーメッセージを表示する
  • 学習時間が0以下の場合はエラーメッセージを表示する
  • 投稿成功時に flash() でメッセージを表示する

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── app.py
└── application/
    ├── __init__.py
    ├── routes/
    │   ├── __init__.py
    │   ├── main.py
    │   └── logs.py
    ├── static/
    │   ├── css/
    │   │   └── style.css
    │   └── images/
    │       └── default.svg
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        └── logs/
            ├── list.html
            ├── detail.html
            └── form.html
```

### 追加される主な役割

- `application/routes/logs.py`:学習ログの一覧、詳細、新規投稿処理
- `templates/logs/form.html`:学習ログ投稿フォーム

完成品質ゴール

  • /logs/new にアクセスすると、学習ログ投稿フォームが表示される。
  • フォームから送信したタイトル、本文、カテゴリ、学習時間、学習日がアプリ側で受け取れる。
  • 入力された学習ログが一覧ページに追加され、投稿後に確認できる。
  • タイトル未入力や学習時間が0以下の場合、登録されずにエラーメッセージが表示される。
  • 投稿が成功した場合、成功メッセージが表示される。
  • フォーム送信後はリダイレクトされ、再読み込みによる二重投稿が起こりにくい構成になっている。

5. Flaskデータベース連携(SQLAlchemy)

演習テーマ

学習ログをデータベースに保存する

演習問題

これまでリストで管理していた学習ログを、SQLiteデータベースに保存できるようにしてください。

作成するモデル

class StudyLog(db.Model):
    id
    title
    body
    category
    study_time
    studied_on
    created_at

実装する機能

  • 学習ログの登録
  • 学習ログ一覧表示
  • 学習ログ詳細表示
  • 学習ログ編集
  • 学習ログ削除

要件

  • Flask-SQLAlchemyを導入する
  • SQLiteを使用する
  • 投稿フォームからデータベースに保存する
  • 一覧ページではDBからログを取得して表示する
  • 詳細ページではIDをもとに1件取得する
  • 存在しないIDの場合は404を返す

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── config.py
├── app.py
├── instance/
│   └── studylog.sqlite        # 実行時に生成されるDBファイル
└── application/
    ├── __init__.py
    ├── models.py
    ├── routes/
    │   ├── __init__.py
    │   ├── main.py
    │   └── logs.py
    ├── static/
    │   ├── css/
    │   │   └── style.css
    │   └── images/
    │       └── default.svg
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        └── logs/
            ├── list.html
            ├── detail.html
            └── form.html
```

### 追加される主な役割

- `config.py`:DB接続設定などのアプリ設定
- `application/models.py`:`StudyLog`モデルを定義
- `instance/studylog.sqlite`:SQLiteのDBファイル。通常はGit管理しない

完成品質ゴール

  • 学習ログのデータがPythonのリストではなく、SQLiteデータベースに保存される。
  • アプリを再起動しても、登録済みの学習ログが消えずに表示される。
  • 学習ログの新規作成、一覧表示、詳細表示、編集、削除が一通り動作する。
  • 存在しないログIDにアクセスした場合は、適切に404エラーとして処理される。
  • StudyLogモデルが定義され、タイトル、本文、カテゴリ、学習時間、学習日、作成日時が管理できる。
  • データベース操作の処理が整理されており、今後モデルを追加しやすい状態になっている。

6. Flaskデータベースマイグレーション

演習テーマ

学習ログモデルに項目を追加する

演習問題

StudyLogアプリにマイグレーションを導入し、既存の学習ログテーブルに新しいカラムを追加してください。

追加するカラム

  • difficulty:学習内容の難易度
  • status:未完了、学習中、完了
  • updated_at:更新日時

要件

  • Flask-Migrateを導入する
  • 初回マイグレーションを作成する
  • 既存モデルにカラムを追加する
  • マイグレーションファイルを作成する
  • DBに変更を反映する
  • フォームにも新しい入力項目を追加する
  • 一覧・詳細ページに追加項目を表示する

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── config.py
├── app.py
├── migrations/
│   ├── README
│   ├── alembic.ini
│   ├── env.py
│   ├── script.py.mako
│   └── versions/
│       └── xxxxxxxxxxxx_add_study_log_fields.py
├── instance/
│   └── studylog.sqlite
└── application/
    ├── __init__.py
    ├── models.py
    ├── routes/
    │   ├── __init__.py
    │   ├── main.py
    │   └── logs.py
    ├── static/
    │   ├── css/
    │   │   └── style.css
    │   └── images/
    │       └── default.svg
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        └── logs/
            ├── list.html
            ├── detail.html
            └── form.html
```

### 追加される主な役割

- `migrations/`:DB変更履歴を管理するフォルダ
- `migrations/versions/`:実際のマイグレーションファイルが入る場所

完成品質ゴール

  • Flask-Migrateが導入され、データベース構造の変更をマイグレーションで管理できる。
  • 既存の StudyLog テーブルに、難易度、ステータス、更新日時のカラムが追加されている。
  • マイグレーション実行後も、既存の学習ログデータが消えずに保持されている。
  • 投稿フォーム・編集フォームで、難易度とステータスを入力または選択できる。
  • 一覧ページや詳細ページで、新しく追加した項目が表示される。
  • モデル変更、マイグレーション作成、DB反映の流れがプロジェクト内で再現できる状態になっている。

7. Flaskでユーザー認証システム

演習テーマ

ユーザー登録・ログイン機能を追加する

演習問題

StudyLogにユーザー認証機能を追加し、ログインしたユーザーだけが学習ログを投稿できるようにしてください。

作成する機能

  • ユーザー登録
  • ログイン
  • ログアウト
  • マイページ
  • ログイン必須ページ

作成するモデル

class User(db.Model):
    id
    username
    email
    password_hash
    created_at

要件

  • Flask-Loginを導入する
  • パスワードはハッシュ化して保存する
  • ログイン中のユーザー名をナビゲーションに表示する
  • 未ログイン状態で /logs/new にアクセスしたらログインページへ移動する
  • 投稿したログにユーザーIDを紐づける

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── config.py
├── app.py
├── migrations/
│   └── ...
├── instance/
│   └── studylog.sqlite
└── application/
    ├── __init__.py
    ├── forms.py
    ├── models.py
    ├── routes/
    │   ├── __init__.py
    │   ├── main.py
    │   ├── logs.py
    │   └── auth.py
    ├── static/
    │   ├── css/
    │   │   └── style.css
    │   └── images/
    │       └── default.svg
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        ├── auth/
        │   ├── register.html
        │   ├── login.html
        │   └── mypage.html
        └── logs/
            ├── list.html
            ├── detail.html
            └── form.html
```

### 追加される主な役割

- `application/forms.py`:登録、ログイン、投稿フォームなどのフォーム定義
- `application/routes/auth.py`:認証関連のルーティング
- `templates/auth/`:認証画面用テンプレート
- `application/models.py`:`User`モデルを追加

完成品質ゴール

  • ユーザー登録、ログイン、ログアウトが正常に動作する。
  • パスワードは平文ではなく、ハッシュ化された状態でデータベースに保存される。
  • ログイン中のユーザー名がナビゲーションやマイページに表示される。
  • 未ログインの状態で学習ログ投稿ページにアクセスすると、ログインページへ誘導される。
  • 投稿された学習ログには、投稿者のユーザー情報が紐づいている。
  • ログイン状態に応じて、表示されるメニューや操作できる機能が切り替わる。
  • 認証が必要なページと誰でも見られるページが明確に分けられている。

8. Flaskユーザー権限管理

演習テーマ

自分の投稿だけ編集・削除できるようにする

演習問題

StudyLogに権限管理を追加し、ユーザーごとに操作できる範囲を制限してください。

実装する権限

ユーザー種別できること
未ログイン一覧・詳細の閲覧のみ
一般ユーザー自分の投稿の作成・編集・削除
管理者全ユーザーの投稿管理

要件

  • Userモデルに role カラムを追加する
  • 一般ユーザーと管理者を区別する
  • 投稿者本人だけが編集・削除ボタンを見られる
  • URLを直接入力しても他人の投稿を編集できないようにする
  • 管理者用の /admin/logs ページを作る

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── config.py
├── app.py
├── migrations/
│   └── ...
├── instance/
│   └── studylog.sqlite
└── application/
    ├── __init__.py
    ├── decorators.py
    ├── forms.py
    ├── models.py
    ├── routes/
    │   ├── __init__.py
    │   ├── main.py
    │   ├── logs.py
    │   ├── auth.py
    │   └── admin.py
    ├── static/
    │   ├── css/
    │   │   └── style.css
    │   └── images/
    │       └── default.svg
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        ├── auth/
        │   ├── register.html
        │   ├── login.html
        │   └── mypage.html
        ├── admin/
        │   └── logs.html
        └── logs/
            ├── list.html
            ├── detail.html
            └── form.html
```

### 追加される主な役割

- `application/decorators.py`:管理者チェックなどの共通権限処理
- `application/routes/admin.py`:管理者用ページ
- `templates/admin/logs.html`:管理者向けログ管理画面
- `application/models.py`:`User.role`を追加

完成品質ゴール

  • 一般ユーザーは自分が投稿した学習ログだけを編集・削除できる。
  • 他人の投稿を編集・削除しようとした場合、画面上のボタンが表示されないだけでなく、URLを直接入力しても操作できない。
  • 管理者ユーザーは、すべての学習ログを管理できる。
  • 一般ユーザーが管理者ページにアクセスしようとした場合、403エラーなどで拒否される。
  • ユーザーの role によって、利用できる機能が適切に切り替わる。
  • 権限チェックがビュー関数側にも実装されており、見た目だけに依存しない安全な構成になっている。

9. Flaskデータのリレーションシップ

演習テーマ

コメント・タグ・いいね機能を追加する

演習問題

StudyLogに複数のテーブル間の関係を追加してください。

追加するモデル

class Comment(db.Model):
    id
    body
    user_id
    study_log_id
    created_at

class Tag(db.Model):
    id
    name

class Like(db.Model):
    id
    user_id
    study_log_id

実装する関係

関係内容
User と StudyLog1対多
StudyLog と Comment1対多
User と Comment1対多
StudyLog と Tag多対多
User と Like1対多

要件

  • 学習ログ詳細ページにコメント一覧を表示する
  • ログインユーザーはコメントを投稿できる
  • 学習ログにタグを複数付けられる
  • タグをクリックすると、そのタグのログ一覧を表示する
  • いいねボタンを押すといいね数が増える
  • 同じユーザーが同じ投稿に複数回いいねできないようにする

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── config.py
├── app.py
├── migrations/
│   └── ...
├── instance/
│   └── studylog.sqlite
└── application/
    ├── __init__.py
    ├── decorators.py
    ├── forms.py
    ├── models.py
    ├── routes/
    │   ├── __init__.py
    │   ├── main.py
    │   ├── logs.py
    │   ├── auth.py
    │   └── admin.py
    ├── static/
    │   ├── css/
    │   │   └── style.css
    │   └── images/
    │       └── default.svg
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        ├── auth/
        │   ├── register.html
        │   ├── login.html
        │   └── mypage.html
        ├── admin/
        │   └── logs.html
        └── logs/
            ├── _cards.html
            ├── list.html
            ├── detail.html
            └── form.html
```

### 追加される主な役割

- `application/models.py`:`Comment`、`Tag`、`Like`、中間テーブルを追加
- `templates/logs/detail.html`:コメント一覧、コメントフォーム、いいね表示を追加
- `templates/logs/_cards.html`:ログカード表示を部品化

完成品質ゴール

  • ユーザーと学習ログが1対多の関係で紐づいている。
  • 学習ログ詳細ページにコメント一覧が表示され、ログインユーザーがコメントを投稿できる。
  • コメントには投稿者情報と投稿日時が表示される。
  • 学習ログに複数のタグを付けられる。
  • タグをクリックすると、そのタグが付いた学習ログだけを一覧表示できる。
  • いいねボタンを押すと、学習ログにいいねを付けられる。
  • 同じユーザーが同じ学習ログに複数回いいねできない。
  • User、StudyLog、Comment、Tag、Likeの各モデルが適切なリレーションで接続されている。

10. Flask RESTful APIの基礎

演習テーマ

学習ログをJSON APIで取得できるようにする

演習問題

StudyLogの投稿データを外部から取得できるRESTful APIを作成してください。

作成するAPI

メソッドURL内容
GET/api/logs学習ログ一覧を取得
GET/api/logs/<id>学習ログ詳細を取得
POST/api/logs学習ログを作成
PUT/api/logs/<id>学習ログを更新
DELETE/api/logs/<id>学習ログを削除

要件

  • レスポンスはJSON形式にする
  • 存在しないIDの場合は404 JSONを返す
  • 入力値が不正な場合は400 JSONを返す
  • 一覧APIにはページネーションを追加する
  • API用の簡単な認証チェックを入れる

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── config.py
├── app.py
├── migrations/
│   └── ...
├── instance/
│   └── studylog.sqlite
└── application/
    ├── __init__.py
    ├── decorators.py
    ├── forms.py
    ├── models.py
    ├── routes/
    │   ├── __init__.py
    │   ├── main.py
    │   ├── logs.py
    │   ├── auth.py
    │   ├── admin.py
    │   └── api.py
    ├── static/
    │   ├── css/
    │   │   └── style.css
    │   └── images/
    │       └── default.svg
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        ├── auth/
        │   ├── register.html
        │   ├── login.html
        │   └── mypage.html
        ├── admin/
        │   └── logs.html
        └── logs/
            ├── _cards.html
            ├── list.html
            ├── detail.html
            └── form.html
```

### 追加される主な役割

- `application/routes/api.py`:RESTful API用ルーティング
- `application/models.py`:JSON化しやすいメソッドを必要に応じて追加

完成品質ゴール

  • /api/logs にアクセスすると、学習ログ一覧がJSON形式で取得できる。
  • /api/logs/<id> にアクセスすると、指定した学習ログの詳細情報がJSON形式で取得できる。
  • API経由で学習ログの作成、更新、削除ができる。
  • 存在しないIDを指定した場合、HTMLではなくJSON形式の404エラーが返る。
  • 入力値が不足している場合や不正な場合、JSON形式の400エラーが返る。
  • 一覧APIにはページネーションがあり、大量データでも扱いやすい。
  • APIのレスポンス形式が統一されており、フロントエンドや外部アプリから利用しやすい状態になっている。

11. Flask ファイルアップロード機能

演習テーマ

学習ログに画像を添付できるようにする

演習問題

学習ログ投稿時に、スクリーンショット画像をアップロードできるようにしてください。

実装する内容

  • 投稿フォームに画像アップロード欄を追加
  • アップロード画像を保存
  • 詳細ページに画像を表示
  • 画像がない場合はデフォルト画像を表示

要件

  • multipart/form-data を使う
  • request.files でファイルを受け取る
  • secure_filename() を使う
  • 許可する拡張子は jpg, jpeg, png, gif のみ
  • ファイルサイズ制限を設定する
  • アップロード先は static/uploads/ にする
  • DBにはファイル名を保存する

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── config.py
├── app.py
├── migrations/
│   └── ...
├── instance/
│   └── studylog.sqlite
└── application/
    ├── __init__.py
    ├── decorators.py
    ├── forms.py
    ├── models.py
    ├── routes/
    │   ├── __init__.py
    │   ├── main.py
    │   ├── logs.py
    │   ├── auth.py
    │   ├── admin.py
    │   └── api.py
    ├── static/
    │   ├── css/
    │   │   └── style.css
    │   ├── images/
    │   │   └── default.svg
    │   └── uploads/
    │       └── .gitkeep
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        ├── auth/
        │   ├── register.html
        │   ├── login.html
        │   └── mypage.html
        ├── admin/
        │   └── logs.html
        └── logs/
            ├── _cards.html
            ├── list.html
            ├── detail.html
            └── form.html
```

### 追加される主な役割

- `static/uploads/`:アップロード画像の保存先
- `application/forms.py`:画像アップロード項目を追加
- `application/models.py`:画像ファイル名を保存するカラムを追加
- `templates/logs/form.html`:`multipart/form-data`対応

完成品質ゴール

  • 学習ログ投稿フォームから画像ファイルをアップロードできる。
  • アップロードされた画像が static/uploads/ に保存される。
  • データベースには画像ファイル名が保存される。
  • 学習ログ詳細ページで、アップロードした画像が表示される。
  • 画像が登録されていない学習ログでは、デフォルト画像が表示される。
  • 許可された拡張子以外のファイルはアップロードできない。
  • ファイル名は secure_filename() などを使って安全に保存される。
  • ファイルサイズ制限が設定され、大きすぎるファイルは拒否される。

12. Flaskで画像処理機能

演習テーマ

アップロード画像のサムネイルを自動生成する

演習問題

アップロードされた画像をPillowで処理し、一覧ページ用のサムネイル画像を生成してください。

実装する内容

  • 画像アップロード時にサムネイルを作成
  • 一覧ページではサムネイルを表示
  • 詳細ページでは元画像を表示
  • 画像サイズを自動調整
  • 画像形式を統一する

要件

  • Pillowを導入する
  • サムネイル画像を static/uploads/thumbnails/ に保存する
  • 横幅300px程度のサムネイルを生成する
  • 画像が大きすぎる場合はリサイズする
  • 壊れた画像がアップロードされた場合はエラー表示する

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── config.py
├── app.py
├── migrations/
│   └── ...
├── instance/
│   └── studylog.sqlite
└── application/
    ├── __init__.py
    ├── decorators.py
    ├── forms.py
    ├── models.py
    ├── routes/
    │   ├── __init__.py
    │   ├── main.py
    │   ├── logs.py
    │   ├── auth.py
    │   ├── admin.py
    │   └── api.py
    ├── services/
    │   ├── __init__.py
    │   └── images.py
    ├── static/
    │   ├── css/
    │   │   └── style.css
    │   ├── images/
    │   │   └── default.svg
    │   └── uploads/
    │       ├── .gitkeep
    │       └── thumbnails/
    │           └── .gitkeep
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        ├── auth/
        │   ├── register.html
        │   ├── login.html
        │   └── mypage.html
        ├── admin/
        │   └── logs.html
        └── logs/
            ├── _cards.html
            ├── list.html
            ├── detail.html
            └── form.html
```

### 追加される主な役割

- `application/services/images.py`:画像リサイズ、サムネイル生成処理
- `static/uploads/thumbnails/`:サムネイル画像の保存先

完成品質ゴール

  • 画像アップロード時に、一覧表示用のサムネイル画像が自動生成される。
  • サムネイル画像は static/uploads/thumbnails/ に保存される。
  • 学習ログ一覧ページではサムネイル画像が表示され、詳細ページでは元画像が表示される。
  • 大きすぎる画像は適切なサイズにリサイズされる。
  • 画像の縦横比が不自然に崩れない。
  • 壊れた画像や画像ではないファイルが送信された場合、アプリが停止せずエラーを表示できる。
  • 画像処理のロジックが関数として整理され、再利用しやすい状態になっている。

13. 非同期処理とバックグラウンドタスク

演習テーマ

時間のかかる画像処理をバックグラウンドで実行する

演習問題

画像のサムネイル生成や通知処理を、バックグラウンドタスクとして実行してください。

実装する内容

  • 投稿直後は「画像処理中」と表示する
  • バックグラウンドでサムネイルを生成する
  • 処理完了後にサムネイルを表示する
  • コメント投稿時に通知処理をバックグラウンドで実行する

要件

  • Celery、RQ、または簡易的なバックグラウンド処理を使う
  • StudyLogに image_status カラムを追加する
  • ステータスは pending, processing, completed, failed などにする
  • 処理失敗時はエラーメッセージを表示する
  • 長時間処理をリクエスト内で直接実行しない

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── config.py
├── app.py
├── migrations/
│   └── ...
├── instance/
│   └── studylog.sqlite
└── application/
    ├── __init__.py
    ├── decorators.py
    ├── forms.py
    ├── models.py
    ├── routes/
    │   ├── __init__.py
    │   ├── main.py
    │   ├── logs.py
    │   ├── auth.py
    │   ├── admin.py
    │   └── api.py
    ├── services/
    │   ├── __init__.py
    │   ├── images.py
    │   └── tasks.py
    ├── static/
    │   ├── css/
    │   │   └── style.css
    │   ├── images/
    │   │   └── default.svg
    │   └── uploads/
    │       ├── .gitkeep
    │       └── thumbnails/
    │           └── .gitkeep
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        ├── auth/
        │   ├── register.html
        │   ├── login.html
        │   └── mypage.html
        ├── admin/
        │   └── logs.html
        └── logs/
            ├── _cards.html
            ├── list.html
            ├── detail.html
            └── form.html
```

### 追加される主な役割

- `application/services/tasks.py`:バックグラウンド処理をまとめる場所
- `application/models.py`:画像処理ステータス用のカラムを追加

完成品質ゴール

  • 時間のかかる画像処理が、リクエスト処理内で直接実行されない構成になっている。
  • 投稿直後は「画像処理中」などのステータスが画面に表示される。
  • バックグラウンド処理が完了すると、サムネイルが表示される。
  • 処理ステータスが pending、processing、completed、failed などで管理されている。
  • 画像処理に失敗した場合でもアプリ全体は停止せず、失敗状態として表示できる。
  • コメント投稿後の通知処理など、重い処理をバックグラウンドに切り出せる構成になっている。
  • 同期処理と非同期処理の役割分担が、プログラム上で明確になっている。

14. 外部API連携

演習テーマ

GitHub APIから学習リポジトリ情報を取得する

演習問題

学習ログにGitHubリポジトリURLを登録できるようにし、そのリポジトリ情報を外部APIから取得して表示してください。

実装する内容

  • 学習ログに repo_url を追加
  • GitHub APIを使ってリポジトリ情報を取得
  • 詳細ページに以下を表示

表示する情報

  • リポジトリ名
  • 説明文
  • 使用言語
  • Star数
  • Fork数
  • 最終更新日

要件

  • 外部APIへのリクエスト処理を作る
  • API通信に失敗した場合の処理を入れる
  • タイムアウトを設定する
  • APIレスポンスをそのまま表示せず、必要な情報だけ整形する
  • 取得した情報をDBにキャッシュしてもよい

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── config.py
├── app.py
├── migrations/
│   └── ...
├── instance/
│   └── studylog.sqlite
└── application/
    ├── __init__.py
    ├── decorators.py
    ├── forms.py
    ├── models.py
    ├── routes/
    │   ├── __init__.py
    │   ├── main.py
    │   ├── logs.py
    │   ├── auth.py
    │   ├── admin.py
    │   └── api.py
    ├── services/
    │   ├── __init__.py
    │   ├── images.py
    │   ├── tasks.py
    │   └── github.py
    ├── static/
    │   ├── css/
    │   │   └── style.css
    │   ├── images/
    │   │   └── default.svg
    │   └── uploads/
    │       ├── .gitkeep
    │       └── thumbnails/
    │           └── .gitkeep
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        ├── auth/
        │   ├── register.html
        │   ├── login.html
        │   └── mypage.html
        ├── admin/
        │   └── logs.html
        └── logs/
            ├── _cards.html
            ├── list.html
            ├── detail.html
            └── form.html
```

### 追加される主な役割

- `application/services/github.py`:GitHub APIへのリクエスト処理
- `application/models.py`:リポジトリURL、API取得結果キャッシュ用カラムを追加
- `templates/logs/detail.html`:リポジトリ情報表示欄を追加

完成品質ゴール

  • 学習ログにGitHubリポジトリURLを登録できる。
  • 登録されたURLをもとに、GitHub APIからリポジトリ情報を取得できる。
  • 学習ログ詳細ページに、リポジトリ名、説明文、使用言語、Star数、Fork数、最終更新日が表示される。
  • 外部APIへの通信に失敗した場合でも、アプリが停止せず、代替メッセージを表示できる。
  • APIリクエストにはタイムアウトが設定されている。
  • APIレスポンスをそのまま表示せず、必要な情報だけを整形して表示している。
  • 必要に応じて取得結果をDBに保存し、毎回APIにアクセスしなくても表示できる構成になっている。

15. Flaskにおけるエラーハンドリングとデバッグ

演習テーマ

StudyLogのエラー画面とログ出力を整える

演習問題

StudyLogで発生するエラーを適切に処理し、ユーザーにわかりやすいエラーページを表示してください。

実装する内容

  • 404ページ
  • 403ページ
  • 500ページ
  • フォームエラー表示
  • APIエラーJSON
  • ログ出力

要件

  • 存在しない学習ログにアクセスしたら404ページを表示する
  • 他人の投稿を編集しようとしたら403ページを表示する
  • 予期しないエラーでは500ページを表示する
  • エラー内容をログファイルに出力する
  • 開発環境と本番環境でデバッグ設定を切り替える

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── config.py
├── app.py
├── migrations/
│   └── ...
├── instance/
│   ├── studylog.sqlite
│   └── logs/
│       └── app.log              # 実行時に生成されるログファイル
└── application/
    ├── __init__.py
    ├── decorators.py
    ├── forms.py
    ├── models.py
    ├── routes/
    │   ├── __init__.py
    │   ├── main.py
    │   ├── logs.py
    │   ├── auth.py
    │   ├── admin.py
    │   └── api.py
    ├── services/
    │   ├── __init__.py
    │   ├── images.py
    │   ├── tasks.py
    │   └── github.py
    ├── static/
    │   ├── css/
    │   │   └── style.css
    │   ├── images/
    │   │   └── default.svg
    │   └── uploads/
    │       ├── .gitkeep
    │       └── thumbnails/
    │           └── .gitkeep
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        ├── errors/
        │   ├── 403.html
        │   ├── 404.html
        │   ├── 413.html
        │   └── 500.html
        ├── auth/
        │   ├── register.html
        │   ├── login.html
        │   └── mypage.html
        ├── admin/
        │   └── logs.html
        └── logs/
            ├── _cards.html
            ├── list.html
            ├── detail.html
            └── form.html
```

### 追加される主な役割

- `templates/errors/`:エラー画面用テンプレート
- `instance/logs/app.log`:エラーログ出力先。通常はGit管理しない
- `application/__init__.py`:エラーハンドラー登録、ログ設定を追加

完成品質ゴール

  • 存在しないページにアクセスした場合、専用の404ページが表示される。
  • 権限のない操作をした場合、専用の403ページが表示される。
  • 予期しないエラーが発生した場合、専用の500ページが表示される。
  • ユーザーには分かりやすいエラーメッセージが表示され、内部の詳細なエラー情報は表示されない。
  • エラー内容はログファイルに記録され、開発者が後から確認できる。
  • APIでエラーが発生した場合は、HTMLではなくJSON形式のエラーレスポンスが返る。
  • 開発環境ではデバッグしやすく、本番環境では安全なエラー表示になるよう設定が分けられている。

16. テスト駆動開発(TDD)単体テストからカバレッジ測定まで

演習テーマ

StudyLogの主要機能をテストする

演習問題

StudyLogのルーティング、モデル、フォーム、認証、APIに対してテストを書いてください。

テスト対象

  • トップページが表示される
  • 学習ログ一覧が表示される
  • 学習ログを作成できる
  • 空タイトルでは作成できない
  • ログインできる
  • ログアウトできる
  • 未ログインでは投稿ページにアクセスできない
  • 自分の投稿は編集できる
  • 他人の投稿は編集できない
  • APIでログ一覧を取得できる
  • 存在しないログIDでは404になる

要件

  • pytestを使う
  • テスト用DBを用意する
  • fixtureを使ってテストデータを作る
  • TDD形式で、先に失敗するテストを書いてから実装する
  • カバレッジを測定する
  • カバレッジ70%以上を目標にする

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── config.py
├── app.py
├── migrations/
│   └── ...
├── instance/
│   ├── studylog.sqlite
│   └── logs/
│       └── app.log
├── tests/
│   ├── __init__.py
│   ├── conftest.py
│   ├── test_basic.py
│   ├── test_auth.py
│   ├── test_logs.py
│   └── test_api.py
└── application/
    ├── __init__.py
    ├── decorators.py
    ├── forms.py
    ├── models.py
    ├── routes/
    │   ├── __init__.py
    │   ├── main.py
    │   ├── logs.py
    │   ├── auth.py
    │   ├── admin.py
    │   └── api.py
    ├── services/
    │   ├── __init__.py
    │   ├── images.py
    │   ├── tasks.py
    │   └── github.py
    ├── static/
    │   ├── css/
    │   │   └── style.css
    │   ├── images/
    │   │   └── default.svg
    │   └── uploads/
    │       ├── .gitkeep
    │       └── thumbnails/
    │           └── .gitkeep
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        ├── errors/
        │   ├── 403.html
        │   ├── 404.html
        │   ├── 413.html
        │   └── 500.html
        ├── auth/
        │   ├── register.html
        │   ├── login.html
        │   └── mypage.html
        ├── admin/
        │   └── logs.html
        └── logs/
            ├── _cards.html
            ├── list.html
            ├── detail.html
            └── form.html
```

### 追加される主な役割

- `tests/conftest.py`:テスト用アプリ、テスト用DB、ログイン補助などのfixture
- `tests/test_basic.py`:基本ページのテスト
- `tests/test_auth.py`:ユーザー登録、ログイン、ログアウトのテスト
- `tests/test_logs.py`:学習ログCRUDと権限のテスト
- `tests/test_api.py`:REST APIのテスト

完成品質ゴール

  • トップページ、一覧ページ、詳細ページなどの主要ルーティングに対するテストが用意されている。
  • 学習ログの作成、編集、削除に対するテストが用意されている。
  • バリデーションエラーが正しく発生するかをテストできている。
  • ユーザー登録、ログイン、ログアウトのテストが用意されている。
  • 未ログイン時や権限不足時にアクセス制限されることをテストできている。
  • APIの正常レスポンスとエラーレスポンスをテストできている。
  • テスト用データベースを使い、本番データに影響を与えずにテストを実行できる。
  • pytestでテストを実行でき、カバレッジ結果を確認できる。
  • 主要機能について、カバレッジ70%以上を目指せる状態になっている。

17. セキュリティ対策:CSRF・XSS・SQLインジェクション

演習テーマ

StudyLogを安全なWebアプリに改善する

演習問題

StudyLogに対して、基本的なWebセキュリティ対策を実装してください。

対策する内容

攻撃対策
CSRFFlask-WTFのCSRFトークン
XSSJinja2の自動エスケープ、入力値のサニタイズ
SQLインジェクションSQLAlchemyの安全なクエリ
不正アップロード拡張子・MIMEタイプ・サイズチェック
権限突破サーバー側で所有者チェック
パスワード漏えいハッシュ化と安全な認証処理

要件

  • 投稿・編集・削除フォームにCSRF対策を入れる
  • コメント欄に <script>alert(“xss”)</script> を入力しても実行されないようにする
  • 検索機能を追加し、安全なクエリで検索する
  • ファイルアップロード時に危険なファイルを拒否する
  • パスワードを平文保存しない
  • 管理者ページに一般ユーザーがアクセスできないようにする

ディレクトリ

studylog/
├── README.md
├── requirements.txt
├── .env.example
├── config.py
├── app.py
├── migrations/
│   └── ...
├── instance/
│   ├── studylog.sqlite
│   └── logs/
│       └── app.log
├── tests/
│   ├── __init__.py
│   ├── conftest.py
│   ├── test_basic.py
│   ├── test_auth.py
│   ├── test_logs.py
│   ├── test_api.py
│   └── test_security.py
└── application/
    ├── __init__.py
    ├── decorators.py
    ├── forms.py
    ├── models.py
    ├── routes/
    │   ├── __init__.py
    │   ├── main.py
    │   ├── logs.py
    │   ├── auth.py
    │   ├── admin.py
    │   └── api.py
    ├── services/
    │   ├── __init__.py
    │   ├── images.py
    │   ├── tasks.py
    │   └── github.py
    ├── static/
    │   ├── css/
    │   │   └── style.css
    │   ├── images/
    │   │   └── default.svg
    │   └── uploads/
    │       ├── .gitkeep
    │       └── thumbnails/
    │           └── .gitkeep
    └── templates/
        ├── base.html
        ├── index.html
        ├── about.html
        ├── errors/
        │   ├── 403.html
        │   ├── 404.html
        │   ├── 413.html
        │   └── 500.html
        ├── auth/
        │   ├── register.html
        │   ├── login.html
        │   └── mypage.html
        ├── admin/
        │   ├── logs.html
        │   └── users.html
        └── logs/
            ├── _cards.html
            ├── list.html
            ├── detail.html
            └── form.html
```

### 追加される主な役割

- `.env.example`:環境変数のサンプル
- `tests/test_security.py`:CSRF、XSS、権限、アップロード制限などのテスト
- `templates/admin/users.html`:必要に応じて管理者向けユーザー管理画面
- `application/forms.py`:Flask-WTFによるCSRF対応フォーム
- `application/routes/logs.py`:検索機能と安全なクエリ処理を追加
- `application/decorators.py`:権限チェックをさらに明確化

完成品質ゴール

  • 投稿、編集、削除などのフォームにCSRF対策が実装されている。
  • CSRFトークンがない不正なリクエストは拒否される。
  • コメント欄や投稿本文に <script> を入力しても、JavaScriptとして実行されない。
  • ユーザー入力を画面に表示する箇所で、XSS対策が有効になっている。
  • 検索機能ではSQL文字列を直接連結せず、安全なクエリでデータを取得している。
  • 危険な拡張子やMIMEタイプのファイルはアップロードできない。
  • 他人の投稿を編集・削除できないよう、サーバー側で所有者チェックが行われている。
  • 管理者ページには管理者ユーザーだけがアクセスできる。
  • パスワードはハッシュ化され、平文では保存されていない。
  • 基本的な攻撃パターンを試しても、アプリが不正操作や情報漏えいを起こしにくい状態になっている。