Djangoで、フォームの内容を保持する(セッション編)

Twitterでシェア FaceBookでシェア はてなブックマークでシェア

Python - Django
2018年11月22日21:14に更新(約21日前)
2018年10月22日2:42に作成(約52日前)

旧ブログ移行記事です。

概要

Djangoで、ユーザー情報の入力後に確認画面を表示したいと思います。
ユーザー情報が入ったPOSTデータをセッションに保存する方法を使いますが、中々に便利です。

見た目

トップページは、ユーザーの一覧が表示されています。

ユーザー情報入力画面で入力し、送信を押すと

確認画面で、内容を確認できます。

戻るで入力画面に戻ると、ユーザー名は既に入力済みの状態です。画像のように、パスワードも入力済みにして戻らせることもできます。

送信では、データが正しく追加されます。

ソースコード

urls.py

from django.urls import path
from . import views

app_name = 'register'

urlpatterns = [
    path('', views.UserList.as_view(), name='user_list'),
    path('user_data_input/', views.user_data_input, name='user_data_input'),
    path('user_data_confirm/', views.user_data_confirm, name='user_data_confirm'),
    path('user_data_create/', views.user_data_create, name='user_data_create'),
]

forms.py

UserCreationFormを継承した、ユーザー作成用の一般的で、汎用的なフォームです。Bootstrap4対応しています。

from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import get_user_model

User = get_user_model()  # Userモデルの柔軟な取得方法


class UserCreateForm(UserCreationForm):
    """ユーザー登録用フォーム"""

    class Meta:
        model = User
        fields = (User.USERNAME_FIELD,)  # ユーザー名として扱っているフィールドだけ、作成時に入力する

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'

views.py

解説は、コメントのとおりです。 処理がやや複雑なので、クラスビューではなく関数ビューで作成しました。

from django.contrib.auth import get_user_model
from django.shortcuts import render, redirect
from django.views import generic
from django.views.decorators.http import require_POST
from .forms import UserCreateForm

User = get_user_model()


class UserList(generic.ListView):
    """ユーザーを一覧表示。"""
    # デフォルトUserだと、authアプリケーションのuser_list.htmlを探すため、明示的に指定する。
    template_name = 'register/user_list.html'
    model = User


def user_data_input(request):
    """新規ユーザー情報の入力。"""
    # 一覧表示からの遷移や、確認画面から戻るリンクを押したときはここ。
    if request.method == 'GET':
        # セッションに入力途中のデータがあればそれを使う。
        form = UserCreateForm(request.session.get('form_data'))
    else:
        form = UserCreateForm(request.POST)
        if form.is_valid():
            # 入力後の送信ボタンでここ。セッションに入力データを格納する。
            request.session['form_data'] = request.POST
            return redirect('register:user_data_confirm')

    context = {
        'form': form
    }
    return render(request, 'register/user_data_input.html', context)


def user_data_confirm(request):
    """入力データの確認画面。"""
    # user_data_inputで入力したユーザー情報をセッションから取り出す。
    session_form_data = request.session.get('form_data')
    if session_form_data is None:
        # セッション切れや、セッションが空でURL直接入力したら入力画面にリダイレクト。
        return redirect('register:user_data_input')

    context = {
        'form': UserCreateForm(session_form_data)
    }
    return render(request, 'register/user_data_confirm.html', context)


@require_POST
def user_data_create(request):
    """ユーザーを作成する。"""
    # user_data_inputで入力したユーザー情報をセッションから取り出す。
    # ユーザー作成後は、セッションを空にしたいのでpopメソッドで取り出す。
    session_form_data = request.session.pop('form_data', None)
    if session_form_data is None:
        # ここにはPOSTメソッドで、かつセッションに入力データがなかった場合だけ。
        # セッション切れや、不正なアクセス対策。
        return redirect('register:user_data_input')

    form = UserCreateForm(session_form_data)
    if form.is_valid():
        form.save()
        return redirect('register:user_list')

    # is_validに通過したデータだけセッションに格納しているので、ここ以降の処理は基本的には通らない。
    context = {
        'form': form
    }
    return render(request, 'register/user_data_input.html', context)

base.html

Bootstrap4を使った、よくあるbase.html

<!doctype html>
<html lang="ja">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">

    <title>セッションを使った確認画面</title>
  </head>
  <body>
    <div class="container mt-3">
        {% block content %}{% endblock %}
    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
  </body>
</html>

user_list.html

ユーザー作成ページへのリンクと、ユーザーの一覧が表示されるページ。

{% extends "register/base.html" %}
{% block content %}
<p><a href="{% url 'register:user_data_input' %}">ユーザー作成</a></p><hr>
{% for user in user_list %}
    <p>ユーザー名: {{ user.username }}</p><hr>
{% endfor %}
{% endblock %}
[end]


[filter h3]user_data_input.html[end]
ユーザー情報の入力ページ
[filter code]
{% extends "register/base.html" %}
{% block content %}
<form action="" method="POST">
    {{ form.non_field_errors }}
    {% for field in form %}
    <div class="form-group">
        <label for="{{ field.id_for_label }}">{{ field.label_tag }}</label>
        {{ field }}
        {{ field.errors }}
    </div>
    {% endfor %}
    {% csrf_token %}
    <button type="submit" class="btn btn-primary btn-lg">送信</button>
</form>
{% endblock %}

user_data_confirm.html

確認ページです。

{% extends "register/base.html" %}
{% block content %}
    {% for field in form %}
    <div class="form-group">
        <label for="{{ field.id_for_label }}">{{ field.label_tag }}</label>
        {{ field.value }}
    </div>
    {% endfor %}

    <a href="{% url 'register:user_data_input' %}" class="btn btn-primary btn-lg">戻る</a>
    <hr>
    <form action="{% url 'register:user_data_create' %}" method="POST">
        <button type="submit" class="btn btn-primary btn-lg">送信</button>
        {% csrf_token %}
    </form>
{% endblock %}

ここは、入力された情報を表示するための部分です。{{ field.value }}で、値やテキストだけ取り出すことができます(input要素は生成しない)。生のパスワードが表示されて気持ち悪い場合は、各フィールドを直接書いていくとよいでしょう。

    {% for field in form %}
    <div class="form-group">
        <label for="{{ field.id_for_label }}">{{ field.label_tag }}</label>
        {{ field.value }}
    </div>
    {% endfor %}

以下は戻るボタンです。入力内容はセッションに保存済みなので、単純にアンカータグで戻れます。

 <a href="{% url 'register:user_data_input' %}" class="btn btn-primary btn-lg">戻る</a>

送信はPOSTで送信しますが、入力内容をセッションに保存しているので、入力欄などは特に必要ありません。入力内容はセッションから復元します。 {% csrf_token %}とsubmitなボタンだけで大丈夫です。

    <form action="{% url 'register:user_data_create' %}" method="POST">
        <button type="submit" class="btn btn-primary btn-lg">送信</button>
        {% csrf_token %}
    </form>

戻った際に、パスワードも入力済みにしたい場合

forms.pyの__init__内を、以下のようします。widget.render_value = Trueで、input type="password"の場合でも入力欄がクリアされません。

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['password1'].widget.render_value = True
        self.fields['password2'].widget.render_value = True
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'
Twitterでシェア FaceBookでシェア はてなブックマークでシェア

記事にコメントする