1-18. 確認画面付きのトピック作成画面を作る

今回のテーマは「確認画面付きのトピック作成画面を作る」です。確認画面つき画面とはユーザーの入力した内容を一度表示して必要に応じて入力画面に戻ることができる画面のことです。アンケートページや申込みページでは頻繁に使われますね。これまで使ってきたCreateViewに少し手を加えるだけで簡単に確認画面つきの登録画面が作れますよ。

はじめにお断りしておくと、確認画面の作り方は色々な手法があって、今回ご紹介する方法はあくまで1つの例と捉えていただければと思います。データ保持にセッションを使ったり、URLをページ毎に分ける場合もありますし、ページ遷移はフロントのみ(バックエンドはAPIのみ担う)で対応するなど、ケースごとに対応が異なります。(これは確認画面に限った話ではないですが)

※本ページはFormViewとCreateViewを使うまで読まれた方を対象としています。そのためサンプルソースコードが省略されている場合があります。

確認事項

まず、現状を確認しましょう。ここまで説明のためにビューに色々なクラスを作成してきたので、少し整理します。まず、ビューですが、thread/views.pyにはTopicCreateViewクラスが以下のように書かれている筈です。

thread/views.py(一部抜粋)


class TopicCreateView(CreateView):
    template_name = 'thread/create_topic.html'
    form_class = TopicModelForm
    model = Topic
    success_url = reverse_lazy('base:top')

そしてthread/urls.pyはlocalhost:8080/create_topic/にアクセスされた場合にトピック作成画面を表示するように以下の様になっています。
thread/urls.py(一部抜粋)


urlpatterns = [
    path('create_topic/', views.TopicCreateView.as_view(), name='create_topic'),
    # path('create_topic/', views.topic_create, name='create_topic'),
    path('/', views.TopicTemplateView.as_view(), name='topic'),
]

登録が成功した場合にはbaseアプリケーションのTOPが表示され、登録されたトピックが一番上に表示されるようになっていますね。

確認画面テンプレートの作成

では確認用の画面を作るわけですからテンプレートを追加しましょう。今回はtemplates/thread/confirm_topic.htmlと名付けましょう。

templates/thread/confirm_topic.html



{% extends 'base/base.html' %}
{% block title %}トピック作成 - {{ block.super }}{% endblock %}
{% block content %}
<div class="ui grid stackable">
    <div class="eleven wide column">
        <div class="ui breadcrumb">
            <a href="{% url 'base:top' %}" class="section">TOP</a>
            <i class="right angle icon divider"></i>
            <a class="active section">トピック作成</a>
        </div>
        <div class="ui segment">
            <div class="content">
                <div class="header"><h3>トピック作成</h3></div>
                <p>内容を確認してください</p>
                <table  class="ui celled table table table-hover" >
                    <tr><td>タイトル</td><td>{{form.title.value}}</td></tr>
                    <tr><td>お名前</td><td>{{form.user_name.value}}</td></tr>
                    <tr><td>カテゴリー</td><td>{{form.cleaned_data.category}}</td></tr>
                    <tr><td>本文</td><td><pre>{{form.message.value}}</pre></td></tr>
                </table>
                <form class="ui form" action="{% url 'thread:create_topic' %}" method="POST">
                    {% csrf_token %}
                    {% for field in form %}
                        {{field.as_hidden}}
                    {% endfor %}
                    <button class="ui button grey" type="submit" name="next" value="back">戻る</button>
                    <button class="ui button orange" type="submit" name="next" value="create">作成</button>
                </form>
            </div>
        </div>
    </div>
    {% include 'base/sidebar.html' %}
</div>
{% endblock %}

注目点は3つあります。まず、カテゴリーのところだけ書き方が変わっていますね。form.category.valueにはカテゴリーIDの数値しか入っておらず、確認画面で表示してもユーザーには分かりません。そこでform_valid関数通過後に生成される検証済みのデータ(cleaned_data)からカテゴリーを取得して表示しています。

二つ目は{{field.as_hidden}}ですね。これは{{form.as_p}}と同様にHTMLを返す関数で、インプットタグのタイプをhiddenとしてくれます。確認画面を作る場合セッションにデータを保持する方法もありますが、今回はhiddenのインプットタグで再度POSTする方式を取ります。

3つ目ですが、戻るボタンと作成ボタンのname属性とvalue属性ですね。つまりPOSTする度にnextパラメータに次にどちらに進むかを司令を出すというわけです。

次に既に作成済みのtemplates/thread/create_topic.htmlにも手を加えましょう。
templates/thread/create_topic.html(一部抜粋)



- <button type="submit" class="ui button">作成</button>
+ <button type="submit" class="ui button" name="next" value="confirm">作成</button>

先程と同様にname属性とvalue属性を追加しました。つまり確認画面に進めという指示を送るということです。

ビューの作成

ではビューの作成に入っていきましょう。既に作成済みのTopicCreateViewを改良していきます。TopicCreateViewはDjagnoのクラスベースビューであるCreateViewを継承したクラスです。

thread/views.py(一部抜粋)


class TopicCreateView(CreateView):
    template_name = 'thread/create_topic.html'
    form_class = TopicModelForm
    model = Topic
    success_url = reverse_lazy('base:top')

    def form_valid(self, form):
        ctx = {'form': form}
        if self.request.POST.get('next', '') == 'confirm':
            return render(self.request, 'thread/confirm_topic.html', ctx)
        if self.request.POST.get('next', '') == 'back':
            return render(self.request, 'thread/create_topic.html', ctx)
        if self.request.POST.get('next', '') == 'create':
            return super().form_valid(form)
        else:
            # 正常動作ではここは通らない。エラーページへの遷移でも良い
            return redirect(reverse_lazy('base:top'))

ソースコードを見たままなのですが、基本的に手を加えたのはform_valid関数のオーバーライドのみです。POSTされたnext値によって場合わけして表示するテンプレートを分けています。’confirm’の場合は先程作成したconfirm_topic.htmlを表示しています。’back’であった場合には受けたformをそのまま渡して入力画面であるtopic_create.htmlを表示しています。これにより、ユーザーの入力値は保持されたまま表示されます。’create’であった場合にはCreateViewのform_valid関数を呼び出して保存処理を行いsuccess_urlに遷移します。これらどれでもない場合は異常系動作となりますが、今回はトップページに飛ばしました。エラーページなどを作成してそちらに飛ばしても良いと思います。

確認してみましょう。
入力画面

確認画面

登録後

最後に

データ検証をフォームが担ってくれているので、基本的にはビューの仕事は見せるべきデータを揃えてテンプレート渡すだけ、という分かりやすい例かなと考えています。一章もそろそろ大詰めですね。次回はカテゴリ毎にトピック一覧表示するページをを作りましょう。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です