本文へスキップ
BecomeCoder

Djangoコース · 第6章 アプリを組み立てる · レッスン28

登録フォームで作る ― フォーム→保存→リダイレクト

ブラウザで完結

導入

いよいよ「書き込み」です。フォームから記事を投稿し、保存して、一覧へ戻る。第4章のフォームと第5章のモデルが、ここで1つにつながります。

説明

GET のときは空のフォームを表示し、POST のときは検証して保存、成功したら一覧へリダイレクト(PRG パターン)します。

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:"}})
    django.setup()

from django.db import models, connection
from django.http import HttpResponse
from django.shortcuts import redirect
from django.urls import path
from django import forms
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)

# 入力フォーム(項目と検証を宣言)
class PostForm(forms.Form):
    title = forms.CharField(max_length=100)
    body = forms.CharField(widget=forms.Textarea)

def post_create(request):
    if request.method == "POST":
        form = PostForm(request.POST)
        if form.is_valid():                       # 検証OKなら
            Post.objects.create(**form.cleaned_data)  # 保存
            return redirect("/")                  # 一覧へ戻す
        return HttpResponse("入力エラー: " + str(form.errors))
    return HttpResponse("(ここに投稿フォームを表示)")

def post_list(request):
    titles = [p.title for p in Post.objects.all()]
    return HttpResponse("記事: " + " / ".join(titles))

urlpatterns = [
    path("", post_list),
    path("create/", post_create),
]

client = Client()
# 正しい投稿 → 保存され、302で / へ
r = client.post("/create/", {"title": "投稿テスト", "body": "本文です"})
print("投稿後ステータス:", r.status_code, "->", r["Location"])
# 不正な投稿(titleが空)→ 検証で弾かれる
print("不正投稿:", client.post("/create/", {"title": "", "body": "x"}).content.decode())
# 一覧を確認
print(client.get("/").content.decode())
  • form = PostForm(request.POST) … 送信データでフォームを作る。
  • form.is_valid() … 検証。通れば form.cleaned_data{"title": ..., "body": ...})に安全な値が入る。
  • Post.objects.create(**form.cleaned_data) … 辞書を展開してそのまま保存。フォームの項目名とモデルのフィールド名をそろえておくと、この1行で書けます。
  • 保存後は redirect("/")。これがフォーム処理の黄金パターンです。

実務では、モデルからフォームを自動生成する ModelForm(レッスン18で紹介)を使うと、form.save() の1行で保存でき、さらに短く書けます。

やってみよう

client.post("/create/", {...})title / body を変えて何度か投稿し、client.get("/") の一覧が増えることを確かめましょう。title を空にすると検証で弾かれ、保存されません。

演習

PostForm に必須の整数フィールド viewsforms.IntegerField)を追加し、投稿時に閲覧数も一緒に保存できるようにしてください。Post モデルにも views = models.IntegerField(default=0) を足す必要があります。正しい投稿 {"title": "x", "body": "y", "views": "5"} が 302 になれば成功です。

ヒント1を見る

モデルに views = models.IntegerField(default=0)、フォームに views = forms.IntegerField() を追加します。

ヒント2を見る

保存は Post.objects.create(**form.cleaned_data) のままで、views も含めて保存されます。テストの POST データに "views": "5" を足します。

実際に動かしてみよう

本文のサンプルや演習のコードは、コードブロック右上の「コピー」ボタンでコピーして、下のエディタに貼り付ければそのまま実行できます。

Python — ライブラリ付きで実行(Pyodide)

numpy / pandas / matplotlib が使える本物のPython(Pyodide)を読み込みます。初回のみ読み込みに少し時間がかかります(以降はブラウザにキャッシュされます)。
スクロールして表示された時点でも自動で読み込まれます。