導入
一覧の各記事をクリックしたら、その記事だけのページへ。/posts/1/、/posts/2/…… という動的 URL と、「無ければ 404」を安全に扱う定番を学びます。
説明
<int:pk> で記事の番号を受け取り、get_object_or_404 で「あれば表示、無ければ 404」を1行で書きます。
import django
from django.conf import settings
if not settings.configured:
settings.configure(DEBUG=True, SECRET_KEY="dev",
ROOT_URLCONF=__name__, ALLOWED_HOSTS=["testserver"],
INSTALLED_APPS=[],
DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
TEMPLATES=[{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"OPTIONS": {"loaders": [("django.template.loaders.locmem.Loader", {
"detail.html": "<h1>{{ post.title }}</h1><p>{{ post.body }}</p>",
})]},
}])
django.setup()
from django.db import models, connection
from django.shortcuts import render, get_object_or_404
from django.urls import path
from django.test import Client
class Post(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
class Meta:
app_label = "blog"
with connection.schema_editor() as schema:
schema.create_model(Post)
Post.objects.create(title="1本目の記事", body="これは本文です。")
Post.objects.create(title="2本目の記事", body="ふたつめ。")
# 詳細ビュー:pk で1件取得。無ければ自動で404。
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, "detail.html", {"post": post})
urlpatterns = [path("posts/<int:pk>/", post_detail)]
client = Client()
print("存在する記事:", client.get("/posts/1/").content.decode())
print("存在しない記事のステータス:", client.get("/posts/999/").status_code) # 404
get_object_or_404(Post, pk=pk)… 主キーpkの記事を取得し、無ければHttp404を送出(=404 ページ)。Post.objects.get(pk=pk)をtry/exceptで囲む定番を、1行にまとめたショートカットです。pkは主キー(primary key)の略で、idと同じもの。Django の慣習でこの名前をよく使います。
一覧(レッスン26)と詳細(このレッスン)がそろえば、あとは一覧の各行に /posts/1/ へのリンクを張るだけで、ブログの読む側は完成です。
やってみよう
client.get("/posts/2/") で2本目の記事を表示したり、/posts/5/ で 404 になることを確かめたりしましょう。記事を増やせば、その番号でアクセスできます。
演習
detail.html を書き換えて、本文の前に「記事番号: N」を表示してください(N は post.id)。出力に 記事番号: 1 が含まれれば成功です。
ヒント1を見る
テンプレートに <p>記事番号: {{ post.id }}</p> を追加します。
ヒント2を見る
post.id と post.pk は同じ値です。