0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Django】ステートレスなHTTPでカートを成立させるセッション設計

Last updated at Posted at 2025-11-28

はじめに

ECサイトのカート機能では、

  • Aさんのカート
  • Bさんのカート

をきちんと分けて管理する必要があります。

そのときに鍵になるのが 「セッション(session)」と「session_id」「Cookie」 です。
ここでは、まず前提となる HTTP / REST / セッション の概念から整理していこうと思います。

セッションの仕組みと概念

まず前提として、WebアプリはHTTPというプロコトルの上で動いています。そして、Webアプリではフロントエンド・バックエンとのやり取りをREST APIという設計スタイルに沿って実装することが多いです。

このREST APIというルールでは

  • 1回のリクエストはそれだけで完結した情報を持つ
  • サーバー側は目のリクエストの状態を前提としない

つまり、HTTP/REST API そのものは

このリクエストがさっきアクセスしてきたあのユーザーが同じ人かどうか、このユーザーが前回どんな操作をしたのか

といった状態(state)を覚えておく仕組みがありません。

REST API についての詳しい内容は、こちらで解説しています。

そのため、何かしらの工夫をしないと、

Aさんが通販サイトでカートに商品を入れても、次のリクエストではまた何も入っていないカートから処理が始まってしまいます。

というように、毎回まっさらな状態から処理されてしまうのです。

これがステートレスな通信です。

しかし、通販サイトでは
ユーザーごとにカートの中身を保持したり、画面遷移をしても多少時間が空いてもカートの中身を復元したいということが多々あります。

このようにユーザーごとに状態を持たせる仕組みがsessionです。
状態をもつ通信のことをステートフルな通信と呼びます。

セッションID・Cookie と REST API の関係

セッションでは、ユーザーごとのアクセスを識別するためにsession_idを使います。REST API自体はステートレスですが、セッションIDを使い状態をサーバ側に置いておくことでユーザーごとの状態を扱えるようにすることができます。

流れのイメージ

1.ユーザーが初回アクセスをする

  • サーバー側で新しいsesson_idを発行する
  • session_idをキーとしてサーバー側のDBなどにユーザーの状態:カートの情報を保存する準備をする
  • 同時に、このsesson_idをCookieとしてブラウザに送る

2.2回目のアクセス

  • ブラウザは、自動的にCookieに保存されたsession_idをリクエストにもたせて送信する
  • サーバーはそのsesson_idを使って、DBから対応するカート情報を取り出す
  • カート情報があればそれを画面に表示する

こういった流れによって本来は、ステートレスなHTTP/REST APIの世界でも、

このリクエストはsesson_id=aeta の人の続きだな、じゃあこの人のカートに入っている情報を表示しよう

というように、ユーザーごとに状態を持った処理(ステートフルな動き)を実現することができます。

カート機能とセッションの関係

カート機能では、この session_id を使って

  • Cart モデルに session_id を保存しておく
  • リクエストから取り出した session_id と一致する Cart を取得する
  • 見つかった Cart に紐づいている CartItem を使って、カート画面を表示する

という形で、

「ユーザーごとに別々のカートを持たせる」

ことができます。

Django では、request.session.session_keysessionid Cookie を使って、
この仕組みをかなりいい感じにラップしてくれているので、
実際の実装では 「どうセッションIDを発行して、Cart と紐づけるか」 を押さえておくのがポイントになります。

モデルを作成

class CartItem(models.Model):
    class Meta:
        db_table = "cart_items"
        constraints = [
            models.UniqueConstraint(
                fields=["cart", "product"],
                name="uniq_cart_product",
            )
        ]

    cart = models.ForeignKey(Cart, verbose_name="カート", on_delete=models.CASCADE)
    product = models.ForeignKey(Product, verbose_name="商品", on_delete=models.CASCADE)
    quantity = models.IntegerField("個数", default=0)

UniqueConstraint で cart + product をユニークにしてる理由
結論:1つのカートに同じ商品業を1レコードにしたいから。

UniqueConstraint(fields=["cart", "product"])をつける理由は、1つの中に同じ商品が複数行で重複して存在しないようにするため
です

例えば、UniqueConstraintをつけないと同じカートに同じ商品が下記のように入る可能性があります

id cart_id product_id quantity
1 1 5 1
2 1 5 2

このようになると、特定のアイテムの個数を出すときにquantityを合計しないといけなくなったり、商品がカートに入っているかを調べる際もfilter(cart=cart, product=product)で複数返る可能性があります

となるとバグが発生しやすくなります。

UniqueConstraintをつけることで
cart + product をユニークにしておくと、

(この cart, この product) の組み合わせは1レコードしか持てない
という制約になるため、

id cart_id product_id quantity
1 1 5 3

この1行にまとめる前提の設計にすることができます。

これによって

「カートに同じ商品を追加する」処理では quantity を更新するだけ

「商品がカートに入っているか」を get_or_create などでシンプルに扱える

といったメリットが得られます。

セッションを使ってカートを区別する処理

先ほど書いた通り、session_id はユーザーごとのリクエストを区別するためのIDです。
Django では、この情報は request オブジェクトの中に入っています。

requestとは

request は、Django がクライアント(ブラウザなど)から受け取った情報をまとめたオブジェクトです。
言い換えると、「ユーザーのリクエスト内容」がすべて詰まっている箱のようなものです。

よく使う属性としては例えば次のようなものがあります。

  • request.method : リクエストの種類("GET" / "POST" など)
  • request.GET : URLクエリのデータ(例: /product?id=3 の id=3)
  • request.POST : フォーム送信(POST)のデータ
  • request.session : セッション情報(ユーザーごとの一時データ)
  • request.user : ログイン中のユーザー情報
  • request.FILES : ファイルアップロードのデータ

request.session の中に session_key が入っているので、これを元に
「カートごとに個別のセッションIDを作成・取得する」 処理を書いていきます。

ただし、毎回すべての処理の中に「セッションIDがなければ作る」というコードを書くのは大変なので、
utils.py を作成し、その中にセッションIDを用意する関数を定義します。

utils.py
# セッションIDを使ってカートを管理する関数
def _ensure_cart_session(request):
    if request.session.session_key is None:
        request.session.create()
    return request.session.session_key

意味としては、

もし request の中に session_key がなければ、新しく session_key を作成する

そのうえで、現在有効な session_key を返す

という関数です。

さいごに

ここまでで、

  • HTTP / REST API がステートレスであること
  • セッションと session_id / Cookie の役割
  • Cart / CartItem モデルと UniqueConstraint の設計意図
  • request.session.session_key を使ってセッションIDを扱う方法

といった、カート機能の土台となる仕組みを整理することができました。

これで、セッション機能を使う準備ができたので
次回は、この session_key を使って

  • カートに商品を追加する処理
  • 既存の商品の数量を更新する処理
  • カートから商品を削除する処理

など、実際のカート更新処理についてまとめていきたいと思います。

↓まとめました。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?