This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 3 years have passed since last update.

[Day 19]確認画面付きのトピック作成画面を作る

Last updated at Posted at 2021-01-24

January 24, 2021
←前回:Day 18 FormViewとCreateViewを使う

「Djangoを学びたい」とのことでありましたら[Day 1]Djangoの開発環境から読むことをおすすめします。

はじめに

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

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

確認事項

まず、現状を確認しましょう。
ここまで説明のためにビューに色々なクラスを作成してきたので、少し整理します。
まず、ビューですが、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('<int:pk>/', 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を継承したクラスです。

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に遷移します。
これらどれでもない場合は異常系動作となりますが、今回はトップページに飛ばしました。
エラーページなどを作成してそちらに飛ばしても良いと思います。

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

image.png

image.png

終わりに

日記ってなかなか悩むんですよね。
普段、英語日記を毎日つけているのですがそこでも「今日は何を書こうかな?」といつも悩んでしまいます。
しかし、日記を書いていると自律神経が調整されとというか自己肯定感が維持できるメンタルになるので好きです。
Django全く関係ありませんでしたね。

それではまたまた

←前回:Day 18 FormViewとCreateViewを使う
→次回:Day 20 カテゴリー毎のトピック一覧画面を作る

0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up