Djangoでショッピングカートを作りたい5

いよいよショッピングカートを作っていきます。
$ python3 manage.py startapp cart

setting.py

INSTALLED_APPS = [
    'shop',
    'search',
    'cart',
    // 省略
]
// 省略
        'DIRS': [os.path.join(BASE_DIR, 'shop', 'templates/'), os.path.join(BASE_DIR, 'search', 'templates/'), os.path.join(BASE_DIR, 'cart', 'templates/')],

cart/models.py

from django.db import models
from shop.models import Product

class Cart(models.Model):
	cart_id = models.CharField(max_length=250, blank=True)
	date_added = models.DateField(auto_now_add=True)

	class Meta:
		db_table = 'Cart'
		ordering = ['date_added']

	def __str__(self):
		return self.cart_id

class CartItem(models.Model):
	product = models.ForeignKey(Product, on_delete=models.CASCADE)
	cart = models.ForeignKey(Cart, on_delete=models.CASCADE)
	quantity = models.IntegerField()
	active = models.BooleanField(default=True)

	class Meta:
		db_table = 'CartItem'

	def sub_total(self):
		return self.product.price * self.quantity

	def __str__(self):
		return self.product.name

$ python3 manage.py makemigrations cart
$ python3 manage.py migrate

cart/urls.py

from django.urls import path
from . import views

app_name = 'cart'

urlpatterns = [
	path('', views.cart_detail, name='cart_detail'),
]

urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('shop/', include('shop.urls')),
    path('search/', include('search.urls')),
    path('cart/', include('cart.urls')),
]

cart/views.py
L session.session_keyでセッションの値を取得する

from django.shortcuts import render, redirect
from .models import Cart, CartItem
from django.core.exceptions import ObjectDoesNotExist

def _cart_id(request):
	cart = request.session.session_key
	if not cart:
		cart = request.session.create()
	return cart

def cart_detail(request, total=0, counter=0, cart_items = None):
	try:
		cart = Cart.objects.get(cart_id=_cart_id(request))
		cart_items = CartItem.objects.filter(cart=cart, active=True)
		for cart_item in cart_items:
			total += (cart_item.product.price * cart_item.quantity)
			counter += cart_item.quantity
	except ObjectDoesNotExist:
		pass

	return render(request, 'cart/cart.html', dict(cart_items = cart_items, total = total, counter = counter))

cart/template/cart/cart.html

{% extends "base.html" %}
{% load static %}

{% block metadescription %}
	This is the shopping cart page.. Proceed to review your items and place the order.
{% endblock %}

{% block title %}
	Cart - Various Product Store
{% endblock %}

{% block content %}
	{% if not cart_items %}
	<div>
		<div class="text-center">
			<br>
			<h1 class="text center my_title">Your shopping cart is empty</h1>
			<br>
			<p class="text-center">
				Please click <a href="{% url 'shop:all_product' %}">here</a> to continue shopping.
			</p>
		</div>
	</div>
	{% else %}
	<div>
		<div class="text-center">
			<br>
			<h1 class="text-center my_title">
				Your shopping cart
			</h1>
		</div>
		<br>
	</div>
	<div class="row mx-auto">
		<div class="col-12 col-sm-12 col-lg-6 text-center">
			<table class="table my_custom_table">
				<thread class="my_custom_thread">
					<tr>
						<th colspan="5">
							Your items
						</th>
					</tr>
				</thread>
				<tbody>
					{% for cart_item in cart_items %}
					<tr>
						<td><a href="cart_item.product.get_absolute_url"><img src="{{cart_item.product.image.url}}" alt="" class="float-left rounded custom_image"></a></td>
						<td class="text-left">
							{{cart_item.product.name}}
							<br>
							SKU: {{cart_item.product.id}}
							<br>
							Unit Price: ${{cart_item.product.price}}
							<br>
							Qty: {{cart_item.quantity}} x ${{cart_item.product.price}}
						</td>
						<td>
							${{cart_item.sub_total}}
						</td>
						{% if cart_item.quantity < cart_item.product.stock %}
						<td>
							<a href="{% url 'cart:add_cart' cart_item.product.id %}" class="custom_a"><i class="fas fa-plus-circle custom_icon"></i></a>

							<a href="" class="custom_a"><i class="fas fa-minus-circle custom_icon"></a>

							<a href="" class="custom_item"><i class="far fa-trash-alt"></a>
						</td>
						{% else %}
						<td>
							<a href="" class="custom_a"><i class="fas fa-minus-circle custom_icon"></a>

							<a href="" class="custom_item"><i class="far fa-trash-alt"></i></a>
						</td>
						<td></td>
						{% endif %}
					</tr>
					{% endfor %}
				</tbody>
			</table>
		</div>
		<div class="col-12 col-sm-12 col-lg-6 text-center">
			<table class="table my_custom_table">
				<thread class="my_custom_thead">
					<tr>
						<th>
							Checkout
						</th>
					</tr>
				</thread>
				<tbody>
					<tr>
						<td>
							Please review your shopping cart item before proceeding with the order payment.
						</td>
					</tr>
					<tr>
						<td class="text-left">
							Your total is: <strong>${{total}}</strong>
						</td>
					</tr>
				</tbody>
			</table>
			<div class="mx-auto">
				<a href="{% url 'shop:all_product' %}" class="btn-secondary btn-block my_custom_button">Continue Shopping</a>
			</div>
		</div>
	</div>
	{% endif %}
{% endblock %}

models.py

from shop.models import Product

def add_cart(request, Product_id):
	product = Product.objects.get(id=product_id)
	try:
		cart = Cart.objects.get(cart_id=_cart_id(request))
	except Cart.DoesNotExist:
		cart = Cart.objects.create(
				cart_id = _cart_id(request)
			)
		cart.save()
	try:
		cart_item = CartItem.objects.get(product=product, cart=cart)
		cart_item.quantity += 1
		cart_item.save()
	except CartItem.DoesNotExist:
		cart_item = CartItem.objects.create(
				product = product,
				quantity = 1,
				cart = cart
			)
		cart_item.save()
	return redirect('cart:cart_detail')

urls.py

urlpatterns = [
	path('add/<int:product_id>/', views.add_cart, name='add_cart'),
	path('', views.cart_detail, name='cart_detail'),
]

product_detail.html

<a class="btn btn-secondary" href="{% url 'cart:add_cart' product.id %}">Add to Cart</a>

cart/views.py

def cart_remove(request, product_id):
	cart = Cart.objects.get(cart_id=_cart_id(request))
	product = get_object_or_404(Product, id=product_id)
	cart_item = CartItem.objects.get(product=product, cart=cart)
	if cart_item.quantity > 1:
		cart_item.quantity -= 1
		cart_item.save()
	else:
		cart_item.delete()
	return redirect('cart:cart_detail')

def full_remove(request, product_id):
	cart = Cart.objects.get(cart_id=_cart_id(request))
	product = get_object_or_404(Product, id=product_id)
	cart_item = CartItem.objects.get(product=product, cart=cart)
	cart_item.delete()
	return redirect('cart:cart_detail')

urls.py

app_name = 'cart'

urlpatterns = [
	path('add/<int:product_id>/', views.add_cart, name='add_cart'),
	path('', views.cart_detail, name='cart_detail'),
	path('remove/<int:product_id>/', views.cart_remove, name='cart_remove'),
	path('full_remove/<int:product_id>/', views.full_remove, name='full_remove')
]

cart/context_processors.py

from .models import Cart, CartItem
from .views import _cart_id

def counter(request):
	item_count = 0
	if 'admin' in request.path:
		return {}
	else:
		try:
			cart = Cart.objects.filter(cart_id=_cart_id(request))
			cart_items = CartItem.objects.all().filter(cart=cart[:1])
			for cart_item in cart_items:
				item_count += cart_item.quantity
		except Cart.DoesNotExist:
			item_count = 0
	return dict(item_count = item_count)

settings.py

            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'cart.context_processors.counter',
            ],

navbar.html

			{% if item_count > 0 %}
			<li class="nav-item">
				<a class="nav-link" href="{% url 'cart:cart_detail' %}">({{item_count}})</a>
			</li>
			{% endif %}

cart.html

						{% if cart_item.quantity < cart_item.product.stock %}
						<td>
							<a href="{% url 'cart:add_cart' cart_item.product.id %}" class="custom_a"><i class="fas fa-plus-circle custom_icon"></i></a>

							<a href="{% url 'cart:cart_remove' cart_item.product.id %}" class="custom_a"><i class="fas fa-minus-circle custom_icon"></a>

							<a href="{% url 'cart:full_remove' cart_item.product.id %}" class="custom_item"><i class="far fa-trash-alt"></a>
						</td>
						{% else %}
						<td>
							<a href="{% url 'cart:cart_remove' cart_item.product.id %}" class="custom_a"><i class="fas fa-minus-circle custom_icon"></a>

							<a href="{% url 'cart:full_remove' cart_item.product.id %}" class="custom_item"><i class="far fa-trash-alt"></i></a>
						</td>
						<td></td>
						{% endif %}

なるほど、ただこれだと、ログアウトした時の処理などが入ってないから、完成には遠いな。Libraryはないのかしら?