Djangoの華麗なるCreateViewの使い方

297, 2021-07-31

目次

Djangoの華麗なるCreateViewの使い方

Djangoではビューを作成する場合、あらかじめDjango側が用意している汎用ビューを使うことが出来ます。
汎用ビューを使うとコードの量を減らすことが可能です。

この記事ではモデルの作成に使われる汎用ビュー、CreateViewの使い方を解説します。
このCreateViewを使うとモデルの投稿ロジックを簡単に書くことが出来ます。
具体的にCreateViewについて↓を見ていきます。

  • 前提とするモデルの定義

  • CreateViewを使ったビューの定義

  • テンプレートファイルの作成

  • ルート情報(urls.py)の編集

  • ビューの改造

関連記事
DjangoのDetailViewのかしこい使い方【Python】
DjangoのListViewのうまい使い方【Python】

参考
CreateView | Generic editing ビュー | Django ドキュメント | Django
django/edit.py at main · django/django

前提とするモデルの定義

今回の解説で使うモデルは、Postというモデルです。
掲示板アプリを作成するという前提で、このような「投稿」を表現するモデルを定義します。

from django.db import models
from django.core.validators import MinLengthValidator


class Post(models.Model):
    name = models.CharField(max_length=32, help_text='投稿者名', validators=[MinLengthValidator(3, '3文字以上です')])
    content = models.TextField(max_length=1024, help_text='投稿内容')

Postのフィールドはnamecontentです。
nameは投稿者名で、contentは投稿内容を表します。
nameにはバリデーターMinLengthValidatorを設定しています。nameの長さが3文字より下だとエラーになります。

CreateViewを使ったビューの定義

Djangoの汎用ビューであるCreateViewを使ってビューを定義します。

from django.shortcuts import render
from django.views.generic import DetailView, CreateView
from core.models import Post
from django.urls import reverse
...

class PostDetail(DetailView):
    model = Post


class PostCreate(CreateView):
    model = Post
    fields = ['name', 'content']

    def get_success_url(self):
        return reverse('posts-detail', kwargs={'pk': self.object.pk})

    ...

PostDetailというのはPostのディティール(詳細)を表示するビューです。
これの詳細については↓をご覧ください。
DjangoのDetailViewのかしこい使い方【Python】

PostCreateが本題のビューで、こちらはCreateViewを継承しています。
PostCreateの属性のmodelにはモデルを代入します。
今回はPostモデルの作成を行うのでPostモデルを入れています。

fieldsにはフォームに表示させるフィールド名を指定します。
今回はユーザーに、Postモデルのnamecontentをフォームに入力してもらうので、これらを指定します。

get_success_url()メソッドを上書きしていますが、これは投稿が成功したときに遷移するページのURLを返すメソッドです。
今回はreverse()を使ってposts-detailのURLへ遷移するようにしています。
posts-detailは後述するurls.pyで定義が見れますが、PostDetailビューのページです。

つまりPostCreateでユーザーにフォームを表示し、そのフォームに入力してもらって投稿を行ってもらい、投稿に成功したらPostDetailビューのページを表示するという感じの設計になります。

テンプレートファイルの作成

PostCreateビューで表示するテンプレートファイルを作成します。
↓のコードをアプリ名/templates/アプリ名/post_form.htmlに保存します。

<form action="{% url 'posts-create' %}" method="POST">
  {% csrf_token %}
  {{ form.as_p }}
  <input type="submit" value="投稿" />
</form>

PostCreateビューは内部でフォームを作成します。
このフォームはコンテキストにformという名前で保存されます。
そのため↑のようにforms.as_pとやるとformの内容が表示されます。

フォームのactionにはposts-createのURLを指定します。
これは後述のurls.pyで定義が見れますが、PostCreateビューを指しています。
methodPOSTです。
csrf_tokenも忘れずに描画しておくようにします。

post_form.htmlというファイル名は今までに指定してませんが、これは暗黙的に決定されます。
PostCreateビューはtemplate_name_suffixという属性を持っています。
これの値はデフォルトでは_formになっています。
PostCreateビューは内部でテンプレートファイル名を合成しますが、このtemplate_name_suffixとモデル名を合体させて合成します。
その結果がpost_formになるわけです。

template_name_suffixの値を変更すればたとえばpost_create_form.htmlというようなファイル名にすることも可能です。

ちなみにPostDetailのテンプレートファイルは↓のようになります。
名前はpost_detail.htmlで保存します。

<p>name: {{ post.name }}</p>
<p>content: {{ post.content }}</p>
(^ _ ^)

手抜きやね

(・ v ・)

そうだね

ルート情報(urls.py)の編集

最後にルート情報を定義します。
name=posts-detailPostDetailビューで、name=posts-createPostCreateビューになります。

from django.contrib import admin
from django.urls import path
from core import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('posts/detail/<int:pk>/', views.PostDetail.as_view(), name='posts-detail'),
    path('posts/create/', views.PostCreate.as_view(), name='posts-create'),
]

以上を定義すると、ブラウザで/posts/create/にアクセスするとフォームが表示されます。
そしてフォームにデータを入力し、投稿ボタンを押すと/posts/detail/<int:pk>/に遷移します。

ビューの改造

CreateViewのメソッドを上書きすることでその振る舞いを上書きすることが出来ます。

post()の上書き

post()メソッドを上書きすると、POST時のビューの振る舞いをハンドリングすることが可能です。

class PostCreate(CreateView):
    ...

    def post(self, request, *args, **kwargs):
        self.object = None
        return super().post(request, *args, **kwargs)

super().post()を呼び出すのを忘れないようにします(returnも忘れずに)。
これが無いとPOSTが処理されません。

get_form()でフォーム詳細を設定

get_form()メソッドを上書きすると、フォームの詳細な設定を行うことが可能です。

class PostCreate(CreateView):
    ...

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        form.fields['name'].label = 'お名前'
        form.fields['content'].required = False
        return form

form_invalid()でエラー時のレスポンスを変更

form_invalid()form.is_valid()Falseの時に呼ばれます。
Trueの時に呼ばれるのはform_valid()です。

form_invalid()は返り値でレスポンスを返し、これが描画されます。
form_invalid()を上書きすれば、エラー時のレスポンスを変更することが可能です。

class PostCreate(CreateView):
    ...

    def form_invalid(self, form):
        response = super().form_invalid(form)
        return response

↑は普通に機能します。↓のようにすると動作を上書きできます。

from django.http import HttpResponse


class PostCreate(CreateView):
    ...

    def form_invalid(self, form):
        return HttpResponse('失敗しました', status=500)

get_context_data()でコンテキストを弄る

get_context_data()を上書きするとコンテキストを弄れます。
コンテキストとは、テンプレートファイルで参照される辞書のことです。
formなどの変数もこのコンテキストに保存されています。

class PostCreate(CreateView):
    ...

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['message'] = 'ご自由に投稿してください。'
        return context

↑の場合はコンテキストにmessageという変数を追加しています。
こうすることでpost_form.htmlmessage変数を参照できます。

<p>{{ message }}</p>
<form action="{% url 'posts-create' %}" method="POST">
  {% csrf_token %}
  {{ form.as_p }}
  <input type="submit" value="投稿" />
</form>

おわりに

今回はDjangoの汎用ビュー、CreateViewについて見てみました。
汎用ビューを使えば楽にアプリを作ることが出来ます。

(^ _ ^)

汎用ビューは正義

(・ v ・)

すんばらし



この記事のアンケートを送信する