URLルーティングと記事詳細ページ
この章では、Djangoの動的ルーティング設定を学び、記事詳細ページとページ間のリンク遷移を実装します。
動的ルーティングとは?
ホームページにはすべての記事が表示されます。各記事には独自の独立した詳細ページが必要です。
URLは次のようになります:/post/1/、/post/2/その数字部分は記事の主キー(pk)です。
動的ルーティングURL内に変数を含めることを許可し、Djangoが自動的に抽出してビュー関数に渡します。
動的ルーティングを設定する
例
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に基づいてデータベースから該当する記事を取得する必要があります。
例
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ページが表示されます。
詳細ページのテンプレートを作成する
例
{% 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 %}
詳細ページのスタイル
例
.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 %}
ホームページの記事カードに詳細ページへのリンクを追加します。
例
<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の組み込みツールなどが詳細ページへのリンクを自動的に見つけられるようになります。
例
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つのページがあり、カードをクリックすると本文を読むために遷移できます。
その他の拡張