DjangoのCookieの取得・保存方法(COOKIES, set_cookie)

306, 2021-08-17

目次

Djangoのcookieの取得・保存方法

DjangoではCookie(クッキー)を扱うことが出来ます。
Cookieはクライアントサイドでも利用できる一時的なデータ保存領域のことで、これを使うとサイトに状態を持たせることが可能です。

Cookieにサイトのクリティカルなデータを保存するのは危険ですが、一時的な重要でないデータを保存するにはCookieはうってつけです。
この記事ではDjangoでCookieを取得・保存する方法について解説したいと思います。
具体的には↓を見ていきます。

  • Cookieとは?

  • DjangoでCookieを取得する

  • DjangoでCookieを保存する

  • クライアントサイド(JavaScript)でCookieを利用する

Cookieとは?

Cookie(クッキー)とはブラウザでWebサイトにアクセスした際に、ブラウザが作成するファイルのことを言います。
このファイルにはWebサイトが指定した情報などが保存されます。
ユーザーはこのファイルを利用することで、ステートレスなHTTPで状態を持った通信を行うことが出来ます。
たとえばECサイトのショッピング・カートの情報などです。カートの情報は別のページに移動した際にも保存されている必要がありますが、こういった状態を持ったロジックにはCookieやセッションなどが使われます。

Cookieはユーザーが自由に削除することが出来ます。そのため揮発的なデータと言えます。
また、Cookieはユーザーが自由に閲覧することが出来るため、Webサイトにとってクリティカルで重要なデータを保存するのには向いていません。
あくまで一時的で、重要でないデータを保存するのにCookieは使われます。
ショッピングカートで言えば、商品のIDなどがそうです。これらのIDは流出しても問題がないと見なされて、Cookieに保存されることがあります。

DjangoでCookieを取得する

DjangoでCookieを取得するには、requestからCOOKIESを参照します。
このCOOKIESは辞書風のオブジェクトで、get()メソッドなどで値を取得することが出来ます。
たとえばcountという名前のCookieの値を取得するには↓のようにします。

def home_view(request):
    ...
    count = request.COOKIES.get('count', 0)
    ...

↑の場合、countには0かあるいは既存のcountの値が入ります。
get()メソッドは第1引数のキーの値を取得しますが、第2引数にはそのキーが存在しなかった場合のデフォルト値を指定することが出来ます。

なぜクライアントサイドで参照されるCookieがサーバーサイドで取得できるのか?

Cookieはブラウザに保存されるものですが、HTTP通信の時にCookieヘッダーというヘッダーにクッキーがセットされます。
このヘッダーはサーバーサイドで解析することが可能です。
そのためDjangoもこのヘッダーを解析して、COOKIESに値を保存しているということになります。

ちなみにCOOKIESWSGIRequestのプロパティです。
このWSGIRequestdjango/core/handlers/wsgi.pyから参照することが出来ます。
COOKIES@cached_propertyでデコレートされたプロパティで、内部では環境からHTTP_COOKIEという生のCookieを取り出し、それをparse_cookie()という関数でパースしています。その結果がCOOKIESの返す値です。

DjangoでCookieを保存する

DjangoでCookieを保存するにはレスポンスのメソッドset_cookie()を使います。

def simple_view(request):
    response = HttpResponse()
    response.set_cookie('count', 1)
    return response

↑の場合、レスポンスのCookieにはcount=1がセットされます。

set_cookie()の構造

set_cookie()は↓のような構造になっています。

def set_cookie(self, key, value='', max_age=None, expires=None, path='/',
               domain=None, secure=False, httponly=False, samesite=None)

set_cookie()は内部ではcookiesというクラスの属性にCookieを保存します。
このcookiesSimpleCookieというオブジェクトで、これはdjango/http/cookie.pyから参照することが出来ます。

expiresの値は以下の値である必要があります。

  • 文字列(正しい日付のフォーマット)

  • ナイーブ(naive)なdatetime.datetimeオブジェクト(UTC)

  • アウェア(aware)なdatetime.datetimeオブジェクト(いずれかのタイムゾーン)

  • (もしdatetime.datetimeであればmax_ageを計算します)

naiveawareについては↓に詳しく書かれています。

Aware オブジェクトと Naive オブジェクト - datetime --- 基本的な日付型および時間型 — Python 3.9.4 ドキュメント

簡単に言うとnaiveはタイムゾーンなどの時間情報を持たないオブジェクトです。
いっぽうawareはタイムゾーンや夏時間情報などを持ちます。
これらのオブジェクトは属性tzinfoNoneならnaiveに、それ以外はawareになります。

クライアントサイド(JavaScript)でCookieを利用する

サーバーサイドで設定したCookieをクライアントサイドで参照したい場合は↓のようなコードを書きます。

from django.http import HttpResponse


def home_view(request):
    response = HttpResponse('''
        <script>
            let cookies = document.cookie.split(';')
            for (const c of cookies) {
                const kv = c.split('=')
                if (kv[0].replace(/^\s+/g, '') === 'count') {
                    document.write(kv[1])
                }
            }
        </script>
    ''')
    count = request.COOKIES.get('count', 0)
    count = int(count) + 1
    response.set_cookie('count', count)
    return response

↑のビューは、まず<script>タグを含んだレスポンスを生成します。
その後にCOOKIESを参照してcountを取得します。
そしてcountの値を1増やして、set_cookieでレスポンスにCookieをセットします。
最後にそのレスポンスを返します。

<script>タグ内ではdocument.cookieの値を参照しています。
これは生のCookieなので目的の値を取り出すにはパースが必要です。
そのため↑のようにfor文などを回してcountの値を取り出すようにしています。

↑のビューを実行すると、画面にcountの値が表示されます。
そしてブラウザを更新するたびに値が増えていきます。

おわりに

今回はDjangoのCookieを見てみました。
Cookieはよく使われるツールの1つですが、Djangoでは簡単に扱うことが可能です。

クッキーが一枚、クッキーが二枚

あれ? 一枚足りないな