詳細画面でカートに入れるボタンをクリックしたときの処理を作成します。

モデル

注文アイテムと注文のモデルを作成します。

app/models.py

from django.conf import settings
from django.db import models


class OrderItem(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    ordered = models.BooleanField(default=False)
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    quantity = models.IntegerField(default=1)

    def get_total_item_price(self):
        return self.quantity * self.item.price

    def __str__(self):
        return f"{self.item.title}{self.quantity}"


class Order(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    items = models.ManyToManyField(OrderItem)
    start_date = models.DateTimeField(auto_now_add=True)
    ordered_date = models.DateTimeField()
    ordered = models.BooleanField(default=False)

    def get_total(self):
        total = 0
        for order_item in self.items.all():
            total += order_item.get_total_item_price()
        return total

    def __str__(self):
        return self.user.email

コード解説

get_total_item_price関数を作ることにより、注文アイテムの金額を取得することができます。

モデルで設定しておくと、テンプレートで容易に取得することができます。

    def get_total_item_price(self):
        return self.quantity * self.item.price

get_total関数を作ることにより、注文の合計金額を取得することができます。

    def get_total(self):
        total = 0
        for order_item in self.items.all():
            total += order_item.get_total_item_price()
        return total

注文が完了すると、orderedが True になります。

ordered = models.BooleanField(default=False)

Admin

管理画面で操作できるように設定します。

app/admin.py

from django.contrib import admin
from .models import Item, OrderItem, Order

admin.site.register(Item)
admin.site.register(OrderItem)
admin.site.register(Order)

マイグレーション実行

モデルを追加したので、マイグレーションが必要になります。

(myvenv) ~$ python3 manage.py makemigrations
(myvenv) ~$ python3 manage.py migrate

URL

商品追加の URL を作成します。

app/urls.py

from django.urls import path
from app import views

urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('product/<slug>', views.ItemDetailView.as_view(), name='product'),
    path('additem/<slug>', views.addItem, name='additem'), # 追加
]

ビュー

商品追加のビューを作成します。

app/views.py

from django.contrib.auth.decorators import login_required
from django.views.generic import ListView, DetailView, View
from .models import Item, OrderItem, Order
from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone


@login_required
def addItem(request, slug):
    item = get_object_or_404(Item, slug=slug)
    order_item, created = OrderItem.objects.get_or_create(
        item=item,
        user=request.user,
        ordered=False
    )
    order = Order.objects.filter(user=request.user, ordered=False)

    if order.exists():
        order = order[0]
        if order.items.filter(item__slug=item.slug).exists():
            order_item.quantity += 1
            order_item.save()
        else:
            order.items.add(order_item)
    else:
        order = Order.objects.create(user=request.user, ordered_date=timezone.now())
        order.items.add(order_item)

    return redirect('order')

コード解説

get_object_or_404関数は、指定したモデルを取得し、モデルが存在しない場合は Http404 を送出します。

item = get_object_or_404(Item, slug=slug)

get_or_createメソッドは、よく使います。

データベースが存在する場合

  • データを取得

データベースが存在しない場合

  • データを登録
    order_item, created = OrderItem.objects.get_or_create(
        item=item,
        user=request.user,
        ordered=False
    )

filter を使用すると、データをリストで取得できます。

[0]を使用して、リストの最初を取得することにより、最新の注文を取得しています。

order = order[0]

すでに商品がある場合は、数量をプラスして、商品がない場合は、新たに追加します。

        if order.items.filter(item__slug=item.slug).exists():
            order_item.quantity += 1
            order_item.save()
        else:
            order.items.add(order_item)

テンプレート

product

カートに入れるボタンの URL を追加します。

app/templates/app/product.html

<a class="btn btn-primary" href="{% url 'additem' item_data.slug %}"
  >カートに入れる</a
>