目次
- Django2実践例 第1章 ブログアプリケーションの作成
- Django2実践例 第2章 ブログ機能の拡張
- Django2実践例 第3章 ブログ機能の更なる拡張
- Django2実践例 第4章 SNSサイトの作成
- Django2実践例 第5章 コンテンツ共有機能
- Django2実践例 第6章 ユーザー行動の追跡
- Django2実践例 第7章 ECサイトの作成
- Django2実践例 第8章 支払いと注文の管理
- Django2実践例 第9章 店舗機能の拡張
- Django2実践例 第10章 オンライン教育プラットフォームの作成
- Django2実践例 第11章 コースコンテンツのレンダリングとキャッシュ
- Django2実践例 第12章 APIの作成
- Django2実践例 第13章 ライブ配信
第7章 ECサイトの作成
前章では、ユーザーのフォローシステムとアクティビティフロー機能を構築し、Djangoのシグナル機能の使用とRedisデータベースでの画像閲覧回数・ランキングの保存方法を学習しました。本章では、基本的なECサイトの構築方法について学習します。本章では、商品カテゴリーディレクトリの作成、セッションを使用したカート機能の実装、カスタムコンテキストマネージャーの使用、Celeryによる非同期タスクの実行について取り上げます。
1. ECサイトプロジェクトの作成
ECサイトプロジェクトを作成します。ユーザーは商品カテゴリーディレクトリを閲覧し、特定の商品をカートに追加し、最後にカートから注文を作成できます。本章で実装するECサイトの主な機能:
- 商品カテゴリーモデルの作成と管理画面への登録、商品カテゴリービューの作成
- ユーザーがサイトを閲覧中にカートに商品を保持するカートシステムの実装
- 注文提出ページの作成
- 注文が成功した後、ユーザーに非同期メール通知を送信
mkdir env
virtualenv env/myshop
source env/myshop/bin/activate
pip install Django==2.0.5
django-admin startproject myshop
cd myshop/
django-admin startapp shop
INSTALLED_APPS = [
# ...
'shop.apps.ShopConfig',
]
1.1 商品カテゴリーモデルの作成
from django.db import models
class ItemCategory(models.Model):
name = models.CharField(max_length=200, db_index=True)
slug = models.SlugField(max_length=200, db_index=True, unique=True)
class Meta:
ordering = ('name',)
verbose_name = 'category'
verbose_name_plural = 'categories'
def __str__(self):
return self.name
class ProductItem(models.Model):
category = models.ForeignKey(ItemCategory, related_name='category', on_delete=models.CASCADE)
name = models.CharField(max_length=200, db_index=True)
slug = models.SlugField(max_length=200, db_index=True)
image = models.ImageField(upload_to='products/%Y/%m/%d', blank=True)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
available = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
ordering = ('name',)
index_together = (('id', 'slug'),)
def __str__(self):
return self.name
1.2 モデルを管理画面に登録
from django.contrib import admin
from .models import ItemCategory, ProductItem
@admin.register(ItemCategory)
class CategoryAdmin(admin.ModelAdmin):
list_display = ['name', 'slug']
prepopulated_fields = {'slug': ('name',)}
@admin.register(ProductItem)
class ProductAdmin(admin.ModelAdmin):
list_display = ['name', 'slug', 'price', 'available', 'created', 'updated']
list_filter = ['available', 'created', 'updated']
list_editable = ['price', 'available']
prepopulated_fields = {'slug': ('name',)}
1.3 商品カテゴリービューの作成
from django.shortcuts import render, get_object_or_404
from .models import ItemCategory, ProductItem
def product_list(request, category_slug=None):
category = None
categories = ItemCategory.objects.all()
products = ProductItem.objects.filter(available=True)
if category_slug:
category = get_object_or_404(categories, slug=category_slug)
products = products.filter(category=category)
return render(request, 'shop/product/list.html',
{'category': category, 'categories': categories, 'products': products})
1.4 商品カテゴリーテンプレートの作成
{% extends "shop/base.html" %}
{% load static %}
{% block title %}
{% if category %}{{ category.name }}{% else %}Products{% endif %}
{% endblock %}
{% block content %}
<div id="sidebar">
<h3>Categories</h3>
<ul>
<li {% if not category %}class="selected"{% endif %}>
<a href="{% url "shop:product_list" %}">All</a>
</li>
{% for c in categories %}
<li {% if category.slug == c.slug %}class="selected"
{% endif %}>
<a href="{{ c.get_absolute_url }}">{{ c.name }}</a>
</li>
{% endfor %}
</ul>
</div>
<div id="main" class="product-list">
<h1>{% if category %}{{ category.name }}{% else %}Products
{% endif %}</h1>
{% for product in products %}
<div class="item">
<a href="{{ product.get_absolute_url }}">
<img src="
{% if product.image %}{{ product.image.url }}{% else %}{% static "img/no_image.png" %}{% endif %}">
</a>
<a href="{{ product.get_absolute_url }}">{{ product.name }}</a>
<br>
${{ product.price }}
</div>
{% endfor %}
</div>
{% endblock %}
2. カート機能の作成
2.1 Djangoセッションモジュールの使用
from django.conf import settings
from shop.models import ProductItem
class ShoppingCart:
def __init__(self, request):
self.session = request.session
cart = self.session.get(settings.CART_SESSION_ID)
if not cart:
cart = self.session[settings.CART_SESSION_ID] = {}
self.cart = cart
2.2 セッション設定
CART_SESSION_ID = 'cart'
2.3 カートデータのセッション保存
def add(self, product, quantity=1, update_quantity=False):
product_id = str(product.id)
if product_id not in self.cart:
self.cart[product_id] = {'quantity': 0, 'price': str(product.price)}
if update_quantity:
self.cart[product_id]['quantity'] = quantity
else:
self.cart[product_id]['quantity'] += quantity
self.save()
def save(self):
self.session.modified = True
2.4 カートビューの作成
from django.views.decorators.http import require_POST
from shop.models import ProductItem
from .shopping_cart import ShoppingCart
from .forms import CartAddProductForm
@require_POST
def cart_add(request, product_id):
cart = ShoppingCart(request)
product = get_object_or_404(ProductItem, id=product_id)
form = CartAddProductForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
cart.add(product=product, quantity=cd['quantity'], update_quantity=cd['update'])
return redirect('cart:cart_detail')
3. 注文処理
3.1 注文モデルの作成
from django.db import models
from shop.models import ProductItem
class Order(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField()
address = models.CharField(max_length=250)
postal_code = models.CharField(max_length=20)
city = models.CharField(max_length=100)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
paid = models.BooleanField(default=False)
def get_total_cost(self):
return sum(item.get_cost() for item in self.items.all())
class OrderItem(models.Model):
order = models.ForeignKey(Order, related_name='items', on_delete=models.CASCADE)
product = models.ForeignKey(ProductItem, related_name='order_items', on_delete=models.CASCADE)
price = models.DecimalField(max_digits=10, decimal_places=2)
quantity = models.PositiveIntegerField(default=1)
def get_cost(self):
return self.price * self.quantity
3.2 注文モデルを管理画面に登録
from django.contrib import admin
from .models import Order, OrderItem
class OrderItemInline(admin.TabularInline):
model = OrderItem
raw_id_fields = ['product']
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = ['id', 'first_name', 'last_name', 'email',
'address', 'postal_code', 'city', 'paid',
'created', 'updated']
list_filter = ['paid', 'created', 'updated']
inlines = [OrderItemInline]
3.3 注文ビューとテンプレートの作成
from django.shortcuts import render
from .models import OrderItem
from .forms import OrderCreateForm
from shopping_cart import ShoppingCart
def order_create(request):
cart = ShoppingCart(request)
if request.method == "POST":
form = OrderCreateForm(request.POST)
if form.is_valid():
order = form.save()
for item in cart:
OrderItem.objects.create(order=order, product=item['product'], price=item['price'],
quantity=item['quantity'])
cart.clear()
return render(request, 'orders/order/created.html', {'order': order})
else:
form = OrderCreateForm()
return render(request, 'orders/order/create.html', {'cart': cart, 'form': form})
4. Celeryによる非同期タスクの実行
4.1 Celeryのインストール
pip install celery==4.1.0
4.2 RabbitMQのインストール
apt-get install rabbitmq
4.3 Celeryのプロジェクト統合
import os
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myshop.settings')
app = Celery('myshop')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
4.4 非同期タスクの追加
from celery import task
from django.core.mail import send_mail
from .models import Order
@task
def order_created(order_id):
order = Order.objects.get(id=order_id)
subject = 'Order {}'.format(order.id)
message = 'Dear {},\n\nYou have successfully placed an order. Your order id is {}.'.format(order.first_name,
order_id)
mail_sent = send_mail(subject, message, 'lee0709@vip.sina.com', [order.email])
return mail_sent
4.5 Celeryの監視
pip install flower==0.9.2