2-9. セッションへのデータの保存と読み出し

今回のテーマは「セッションへのデータの保存と読み出し」です。掲示板アプリもそれっぽくなってきましたね。あと少し頑張りましょう。

今回はセッションの使い方を学ぶための実装を例示しますが、学習用に用意したため掲示板の機能にはあまり影響がありません。予めご了承下さい。

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


セッション

セッションについては公式ドキュメントも参照してください。Djangoのセッションは完全にクッキーベースでありセッションIDをURLとして渡す等の手法は用いません。また、セッションとはブラウザが起動中のみ有効でブラウザと閉じると切れるものと考えている方もいるかも知れませんが、Djangoの標準設定ではセッションはブラウザを閉じても生き続ける永続的なもので、意図的に削除されるか、有効期限(標準設定では2週間)となるまで削除されません。もちろん、ブラウザを閉じたらセッションも切れるようにも設定出来ます。

セッションを使ったトピック作成画面

確認画面付きのトピック作成画面を作るで作成したトピック作成画面をセッションを用いる方式に修正してみましょう。トピック作成画面はユーザー作成画面→確認画面→(トピック作成処理)→TOP画面と遷移していきます。現行処理は確認画面にhiddenのフォームを入れることでユーザー入力画面の情報をトピック作成処理に渡すことにしていました。今回はセッションを使ってこの処理を実装してみます。

まずthread/views.pyに新しいクラスを作りましょう。以下のようなクラスを生成します。
thread/views.py(一部抜粋)
※2020/9/13一部ソースコードを修正しました。


class TocicCreateViewBySession(FormView):
    template_name = 'thread/create_topic.html'
    form_class = TopicModelForm

    def post(self, request, *args, **kwargs):
        ctx = {}
        if request.POST.get('next', '') == 'back':
            if 'input_data' in self.request.session:
                input_data = self.request.session['input_data']
                form = TopicModelForm(input_data)
                ctx['form'] = form
            return render(request, self.template_name, ctx)
        elif request.POST.get('next', '') == 'create':
            if 'input_data' in request.session:
                form = self.form_class(request.session['input_data'])
                form.save()
                # Topic.objects.create_topic(
                #     title=request.session['input_data']['title'],
                #     user_name=request.session['input_data']['user_name'],
                #     category_id=request.session['input_data']['category'],
                #     message=request.session['input_data']['message']
                # )
                request.session.pop('input_data') # セッションに保管した情報の削除
                # メール送信処理は省略
                return redirect(reverse_lazy('base:top'))
        elif request.POST.get('next', '') == 'confirm':
            form = TopicModelForm(request.POST)
            if form.is_valid():
                ctx = {'form': form}
                # セッションにデータを保存
                input_data = {
                    'title': form.cleaned_data['title'],
                    'user_name': form.cleaned_data['user_name'],
                    'message': form.cleaned_data['message'],
                    'category': form.cleaned_data['category'].id,
                }
                request.session['input_data'] = input_data
                ctx['category'] = form.cleaned_data['category']
                return render(request, 'thread/confirm_topic.html', ctx)
            else:
                return render(request, self.template_name, {'form': form})

templates/thread/confirm_topic.html内でhiddenのインプットタグを削除します。
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>{{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 %}

thread/urls.pyも変更します。
thread/urls.py(一部抜粋)


+ path('create_topic/', views.TocicCreateViewBySession.as_view(), name='create_topic'),
- path('create_topic/', views.TocicCreateView.as_view(), name='create_topic'),

これで最初に作成した確認画面付きのトピック作成画面と同等の機能をセッションを使って実装することが出来ました。セッションは基本的にはビュー内部で扱うことが多く、request.sessionに対してキーと値を持たせることで保存します。セッションエンジンにはDB,ファイル, キャッシュがありますが、今回はデフォルト設定されているDBを用いました。変更したい場合には公式ドキュメントのセッションエンジンを設定するを参考にして下さい。

セッションをビュー外部から使用することも出来ます。その場合はビューの外でセッションを使うを参考にして下さい。

最後に

セッションは会員制のサイトなどでユーザー情報を保持しておく際によく用いられます。次回はCookieの扱いについて見ていきたいと思います。

Sponsored Link


「2-9. セッションへのデータの保存と読み出し」への2件のフィードバック

  1. いつも勉強させていただいております。

    今回のテーマでエラーが発生してしまい、色々と調べてみましたが自分ではどうにも解決できなかったので質問いたします。

    掲載されているコードで実行すると、下記のエラーコードが出ます。
    ‘TopicManager’ object has no attribute ‘create_topic’

    thread/models.pyでcreate_topicの定義をしていないのかと思ったのですが、models.pyの変更も行っておりますか?

    ご教示いただけますと幸いです。

    1. SSさん

      返信が遅くなり恐縮です。
      ご指摘の通り誤記がありました。当初、models.pyにcreate_topicを定義し、その関数を利用してトピックの保存をしようと考えましたが、formを利用するほうがシンプルと考えmodels.pyへの変更はしない方針に変更しました。

      この際にcreate_topicを利用したコードは修正すべきでしたが、未変更のまま投稿してしまいました。ご迷惑をおかけしました。投稿のソースは修正しましたのでご確認ください。

コメントを残す

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