URLルーティングと記事詳細ページ

この章では、Djangoの動的ルーティング設定を学び、記事詳細ページとページ間のリンク遷移を実装します。


動的ルーティングとは?

ホームページにはすべての記事が表示されます。各記事には独自の独立した詳細ページが必要です。

URLは次のようになります:/post/1/、/post/2/その数字部分は記事の主キー(pk)です。

動的ルーティングURL内に変数を含めることを許可し、Djangoが自動的に抽出してビュー関数に渡します。


動的ルーティングを設定する

例

# ファイルパス: blog/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
    # int:pk — URL内の整数パラメータ。名前は pk(primary key)
    # name='post_detail' — ルートに名前を付け、テンプレート内で {% url %} から参照
    path('post/<int:pk>/', views.post_detail, name='post_detail'),
]

ルートコンバータの説明:

コンバータサンプルパス一致ルール
<int:pk>/post/3/正の整数に一致
<str:slug>/post/django-guide/文字列(/ を含まない)に一致
<slug:slug>/post/django-guide/英数字、-、_ に一致
<uuid:id>/post/uuid/UUID形式に一致

詳細ページのビューを作成する

詳細ページでは、URL内のpkに基づいてデータベースから該当する記事を取得する必要があります。

例

# ファイルパス: blog/views.py
from django.shortcuts import render, get_object_or_404
from .models import Post

def post_detail(request, pk):
    """記事詳細ページ"""
    # get_object_or_404:単一レコードを取得
    # 見つかればオブジェクトを返し、見つからなければ自動的に404ページを返す(try/except不要)
    post = get_object_or_404(Post, pk=pk)

    context = {
        'post': post,
        'title': f'{post.title} - サンプル 博客'
    }
    return render(request, 'blog/post_detail.html', context)

get_object_or_404はDjangoのベストプラクティスです。次と同等です。Post.objects.get(pk=pk) + try/except Post.DoesNotExistしかしコードはより簡潔です。存在しない記事IDにアクセスすると、エラーではなく親しみやすい404ページが表示されます。


詳細ページのテンプレートを作成する

例

<!-- ファイルパス: blog/templates/blog/post_detail.html -->
{% extends 'blog/base.html' %}

{% block title %}{{ post.title }} - サンプル 博客{% endblock %}

{% block content %}
<article class="post-view">
    <span class="category-tag">{{ post.category.name }}</span>
    <h1>{{ post.title }}</h1>
    <time>{{ post.created_at|date:"Y-m-d" }}</time>

    <!-- 本文は |safe フィルタでHTMLをレンダリングします(Adminで入力したHTMLコンテンツ) -->
    <div class="content">
        {{ post.content|safe }}
    </div>

    <a href="{% url 'index' %}" class="back-link">← ホームに戻る</a>
</article>
{% endblock %}

詳細ページのスタイル

例

/* base.html の style に追記 */
.post-view {
    max-width: 720px;
    margin: 0 auto;
}

.category-tag {
    display: inline-block;
    padding: 4px 12px;
    background: #E8EEF5;
    color: #3D71A6;
    border-radius: 12px;
    font-size: 13px;
    margin-bottom: 12px;
}

.post-view h1 {
    font-size: 32px;
    margin-bottom: 12px;
    line-height: 1.4;
}

.post-view time {
    display: block;
    color: #999;
    font-size: 14px;
    margin-bottom: 30px;
}

.content {
    line-height: 1.8;
    font-size: 16px;
    color: #333;
}

.content h2 {
    margin: 24px 0 12px;
    font-size: 22px;
}

.content p {
    margin-bottom: 12px;
}

.back-link {
    display: inline-block;
    margin-top: 40px;
    color: #2c3e50;
    text-decoration: none;
}

|safeフィルタはDjangoに「このコンテンツは安全なHTMLなので、エスケープせずに直接レンダリングする」と伝えます。safeを使用する際は、コンテンツが信頼できるものであることを確認してください(例:Adminで自分が編集したコンテンツ)。ユーザーが送信したコンテンツには決してsafeを付けないでください。XSSのリスクがあります。


テンプレートでリンクを生成:{% url %}

ホームページの記事カードに詳細ページへのリンクを追加します。

例

<!-- blog/templates/blog/index.html の記事カードを修正 -->
<div class="article-grid">
    {% for post in posts %}
    <a href="{% url 'post_detail' post.pk %}" class="card-link">
        <div class="article-card">
            <div class="card-content">
                <span class="card-category">{{ post.category.name }}</span>
                <h3>{{ post.title }}</h3>
                <p>{{ post.summary|truncatechars:80 }}</p>
                <span class="card-date">{{ post.created_at|date:"Y-m-d" }}</span>
            </div>
        </div>
    </a>
    {% endfor %}
</div>

{% url 'post_detail' post.pk %}ルート名とパラメータに基づいて、正しいURLを自動生成します。

今後ルートが変更された場合(例:/post/に変更/article/)、テンプレート内のリンクは自動的に更新されるため、手動で修正する必要はありません。


モデルに get_absolute_url() を追加する

Postモデルでこのメソッドを定義すると、Admin管理画面やDjangoの組み込みツールなどが詳細ページへのリンクを自動的に見つけられるようになります。

例

# ファイルパス: blog/models.py の Post クラスに追加
from django.urls import reverse

class Post(models.Model):
    # ... フィールド定義 ...
    def __str__(self):
        return self.title

    def get_absolute_url(self):
        # reverse はルート名とパラメータからURLを逆引きします
        return reverse('post_detail', kwargs={'pk': self.pk})

本章のまとめ

この章では、Djangoルーティングの核心(動的ルーティング)を学びました。<int:pk>パラメータの受け渡し、get_object_or_404による安全なクエリ、{% url %}テンプレートタグによるリンク生成、reverseによるURL逆引き。

これでブログにはホームページの一覧と記事詳細ページの2つのページがあり、カードをクリックすると本文を読むために遷移できます。

その他の拡張