はじめに
前章では「URL → View → Template」の流れを学び、blog/urls.py に path('', views.index, name='index') を書いて URLの設定まで終えました。そして最後に「次章ではこの views.index 関数を実装して、画面を表示します」と予告しました。
というわけでこの章では、その予告どおり残りの View(views.py) と Template を実装して、トップページを完成させます。今はトップページにアクセスすると views.index が呼ばれる状態なので、まずはその index 関数の中身から作っていきます。
ポイントは、テンプレートをいきなり1枚の巨大なHTMLで作らないこと。Djangoの公式が推奨する テンプレート継承(共通の枠を base.html にまとめ、各ページはその差分だけ書く)を最初から使います。
「あとで綺麗にすればいいや」で1枚HTMLを量産すると、ヘッダーを直すのに全ページ修正…みたいな地獄になるよ!
最初から base.html に共通部分を集めておけば、1か所直すだけで全ページに反映されるって古事記にもそう書かれてる
Step1. views.pyにindex関数を書く
~/myblog/blog/views.py を開いて、以下に書き換えます。
from django.shortcuts import render
def index(request):
# 特集バナー用のデータ
featured = {
'title': 'AIと一緒に作るDjangoブログ入門',
'summary': 'DockerとDjangoを使ってブログサイトをゼロから作る方法を、AIと一緒に解説します。',
}
# 記事一覧用のダミーデータ
posts = [
{
'title': 'Bootstrap5でデザインを整える',
'date': '2026年5月19日',
'author': '管理者',
'body': 'CSSを書かなくてもBootstrap5を使えば綺麗なデザインが作れます。',
},
{
'title': 'Djangoのテンプレート継承',
'date': '2026年5月20日',
'author': '管理者',
'body': '共通部分を base.html にまとめると、各ページは差分だけ書けばよくなります。',
},
]
context = {
'featured': featured,
'posts': posts,
}
return render(request, 'blog/index.html', context)
views.pyの役割
views.py は データを用意してテンプレートに渡す 役割を持ちます。
今はまだデータベースがないので、Pythonの辞書(dict)とリストでダミーデータを用意しています。第4章でデータベースのモデルを作ったら、ここをDB取得のコードに差し替えます。
render(request, 'blog/index.html', context) の意味:
| 引数 | 意味 |
|---|---|
request |
ブラウザからのリクエスト情報 |
'blog/index.html' |
使うテンプレートのパス |
context |
テンプレートに渡すデータ(辞書) |
辞書のキー名は適当でもいいけど、ここでは summary みたいに「後でDBのフィールド名になりそうな名前」を選んでるよ。第4章でDB化するとき、テンプレート側を書き換えずに済むからラクなの!
Step2. CSSファイルを作る
HTMLにCSSを直接書く(インラインスタイル)のではなく、別ファイルに分けるのが基本です。
mkdir -p ~/myblog/blog/static/blog
touch ~/myblog/blog/static/blog/blog.css
フォルダ構成:
blog/
└ static/
└ blog/ ← テンプレートと同じく「アプリ名」で名前空間化
└ blog.css
templates/blog/ と同じで、static/blog/ とアプリ名のフォルダを挟むのが公式ルールです。複数アプリで同名ファイルがあっても 'blog/blog.css' と一意に指定できます。
~/myblog/blog/static/blog/blog.css の中身(今はこれだけ。後の章で少しずつ増やします):
/* ロゴと見出しは上品なセリフ体に */
.blog-header-logo {
font-family: "Playfair Display", Georgia, "Times New Roman", serif;
font-size: 2.25rem;
}
h1, h2, h3, h4, h5, h6 {
font-family: "Playfair Display", Georgia, "Times New Roman", serif;
}
/* 記事ブロックの余白 */
.blog-post { margin-bottom: 4rem; }
.blog-post-meta { margin-bottom: 1.25rem; color: #8a8a8a; }
Step3. 共通の枠 base.html を作る(テンプレート継承の親)
まず全ページ共通のHTML(<html>・<head>・ヘッダー・フッターなど)を入れる親テンプレートを作ります。
touch ~/myblog/blog/templates/blog/base.html
~/myblog/blog/templates/blog/base.html の中身:
{% load static %}
<!DOCTYPE html>
<html lang="ja" data-bs-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}My Blog{% endblock %}</title>
<link href="https://fonts.googleapis.com/css?family=Playfair+Display:700,900&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="{% static 'blog/blog.css' %}" rel="stylesheet">
</head>
<body>
{# 共通ヘッダー #}
<div class="container">
<header class="border-bottom lh-1 py-3">
<div class="row justify-content-center">
<div class="col-4 text-center">
<a class="blog-header-logo text-body-emphasis text-decoration-none"
href="{% url 'blog:index' %}">My Blog</a>
</div>
</div>
</header>
</div>
{# 各ページの中身がここに入る #}
<main class="container">
{% block content %}{% endblock %}
</main>
{# 共通フッター #}
<footer class="py-5 text-center text-body-secondary bg-body-tertiary">
<p class="mb-0">My Blog - AIと一緒に作るDjango入門</p>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
ポイント解説
① {% load static %}
1行目の {% load static %} は、Djangoの静的ファイル機能({% static %} タグ)を使うためのおまじないです。これがないと下の {% static 'blog/blog.css' %} がエラーになります。
② {% block title %} と {% block content %}
{% block %} は「子テンプレートが上書きできる穴」です。title はページごとのタイトル、content はページごとの本文が入ります。共通の枠だけ base.html に書いて、中身は各ページに任せる――これがテンプレート継承です。
③ data-bs-theme="dark"
Bootstrap5.3の機能で、これ1つでサイト全体がダークテーマになります。bg-body-tertiary や text-body-emphasis といった「テーマに追従する色クラス」を使えば、明/暗の切り替えも自動です。
④ {% url 'blog:index' %}
前章で blog/urls.py に付けた名前(app_name='blog' + name='index')を使って、トップページのURLを自動生成しています。URLを直書き(href="/")せずに名前で参照すると、あとでURL構成を変えても全ページが自動で追従します。
📚 参考URL:Django チュートリアル Part 3 - テンプレートの継承(公式も
base.htmlを親にする継承を推奨しています)
今の base.html はロゴだけのシンプルなヘッダーだけど、第5章で「検索フォーム」と「カテゴリナビ」、サイドバーを足していくよ。SEOタグ(OGPとか)は第7章でこの base.html に追加する予定。今は『共通の枠を1か所にまとめる』感覚を掴めばOK!
Step4. index.html を作る(base.html を継承する子)
次に、トップページ専用の中身だけを書きます。
touch ~/myblog/blog/templates/blog/index.html
~/myblog/blog/templates/blog/index.html の中身:
{% extends 'blog/base.html' %}
{% block title %}My Blog - トップ{% endblock %}
{% block content %}
{# 特集バナー #}
<div class="p-4 p-md-5 mb-4 rounded text-body-emphasis bg-body-secondary">
<div class="col-lg-6 px-0">
<h1 class="display-4 fst-italic">{{ featured.title }}</h1>
<p class="lead my-3">{{ featured.summary }}</p>
</div>
</div>
{# 記事一覧 #}
{% for post in posts %}
<article class="blog-post">
<h2 class="display-5 mb-1">{{ post.title }}</h2>
<p class="blog-post-meta">{{ post.date }} by {{ post.author }}</p>
<p>{{ post.body }}</p>
</article>
{% endfor %}
{% endblock %}
たったこれだけ なのがテンプレート継承の威力です。
- 1行目
{% extends 'blog/base.html' %}で「base.html を土台にする」と宣言 {% block title %}…{% endblock %}で base.html のタイトルを上書き{% block content %}…{% endblock %}の中だけが、base.html の<main>に差し込まれる
<html> や <head>、ヘッダー・フッターは base.html 側にあるので、ここでは一切書かなくていい。ページが増えても同じ枠を使い回せます。
{{ featured.title }} の featured は、Step1のviews.pyで context に入れた辞書だよ。featured.title でその中の 'title' を取り出してる。{% for post in posts %} も同じで、views.pyから渡したリストを1件ずつ回してるの!
Step5. settings.pyにSTATIC_ROOTを追加する
CSSなどの静的ファイルを本番で配信できるように、集約先を設定します。~/myblog/myblog/settings.py の STATIC_URL の下に追記:
STATIC_URL = 'static/'
STATIC_ROOT = BASE_DIR / 'staticfiles' # ← 追加
STATIC_URLとSTATIC_ROOTの違い
| 設定 | 役割 |
|---|---|
STATIC_URL |
HTMLに出力されるURLのプレフィックス(/static/blog/blog.css の /static/ 部分) |
STATIC_ROOT |
collectstatic コマンドでファイルを集める場所のパス |
Step6. collectstaticを実行する
cd ~/myblog
docker compose run --rm web python manage.py collectstatic --noinput
collectstatic は各アプリの static/ フォルダからファイルを集めて staticfiles/ にまとめるコマンドです。
blog/static/blog/blog.css
↓ collectstatic
staticfiles/blog/blog.css ← Nginx がここを配信する
Nginx は staticfiles/ フォルダを直接配信しています(docker/nginx.dev.conf の location /static/ で alias /app/staticfiles/; を設定済み)。
⚠️ 注意:CSSや画像ファイルを追加・変更したときは、毎回この
collectstaticを実行する必要があります。「CSSを直したのに反映されない」ときはこれを忘れていることが多いです。
Step7. 起動確認
cd ~/myblog
docker compose up
ブラウザで http://localhost を開いて、ダークテーマのトップページ(特集バナー+記事2件)が表示されれば成功です!
まとめ
この章で、Djangoの基本の流れがひと通り完成しました。
URL(urls.py) ← 第2章で設定
↓
処理(views.py) ← ダミーデータを用意(この章)
↓
表示(テンプレート) ← base.html(共通の枠)+ index.html(差分)
ポイントは、最初から テンプレート継承(base.html) で組んだこと。共通部分が1か所にまとまっているので、これから章を進めても
- 第5章:base.html に検索フォーム・カテゴリナビ・サイドバーを追加
- 第7章:base.html にSEO(OGP等)を追加
…と、同じ base.html を育てていくだけで済みます。
次章では MySQL にデータを保存する モデル を作り、この章のダミーデータをDBのデータに置き換えます。

