Published Date : 2020年6月15日11:28

Part 7 - Djangoを使って簡単なオンラインショップを作ってみよう
Part 7 - Create a Simple Online Shop with Django


This blog has an English translation


YouTubeにアップした動画、「【Django】Part 7 - Djangoを使って簡単なオンラインショップを作ってみよう」の補足説明の記事です。

Here's a little more about the 「【Django】Part 7 - Create a Simple Online Shop with Django」 video I uploaded to YouTube.

Djangoを使って簡単なログイン機能を持ったオンラインショップのようなサイトを作ってみましょう。 Part7となる今回の動画シリーズでは、新たなモデルを追加して、 カスタマーのショッピングカートの中身を表示するページとその機能、 そして出品者用の商品登録用、編集用ページとその機能のプロトタイプの作成を行います。

Use Django to create a site that looks like an online store with a simple login feature. In this video series, Part 7, We will add a new model and create a prototype of a page that displays the contents of the customer's shopping cart and its function, as well as a page for registering and editing products for sellers and its function.

無駄な説明を省いて、忙しい時でも短時間で理解できるような動画です。

It's a video that can be understood in a short period of time even when you're busy, without any unnecessary explanation.


目次

Table of Contents




① models.pyとmakemigrationsとurls.py
① models.py and makemigrations and urls.py



では取引用のモデルの追加と、出品者ユーザーと出品物の紐づけを行っていきます。

Then, We will add a model for transaction and link the seller user and the item.


Responsive image


demoshop/models.py

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone

class Customer(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)

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

    class Meta:
        verbose_name = "カスタマー"
        verbose_name_plural = "カスタマー"

class Seller(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)

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

    class Meta:
        verbose_name = "出品者"
        verbose_name_plural = "出品者"

class Item(models.Model):
    title = models.CharField(
        max_length=30,
        null=False,
        blank=False,
        unique=False)
    description = models.TextField(
        max_length=90,
        null=False,
        blank=False,
        unique=False)
    price = models.FloatField(
        null=False,
        blank=False,
        unique=False)
    seller = models.ForeignKey(
        Seller,
        on_delete=models.CASCADE)

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "商品"
        verbose_name_plural = "商品"

class Transaction(models.Model):
    item = models.ForeignKey(
        Item,
        on_delete=models.CASCADE
    )
    customer = models.ForeignKey(
        Customer,
        on_delete=models.CASCADE
    )
    paid_amount = models.FloatField()
    timestamp = models.DateField(default=timezone.now)

モデルが少し複雑になってきました。そんな時には画像でモデルの全体像を把握すると良いでしょう。

The model is getting a little more complex. In such a case, it is better to grasp the whole image of the model.

django_extensionsとgraphvizを使用すると簡単にモデルの全体像を可視化できます。

Using django_extensions and graphviz, you can easily visualize the entire model.

$ sudo apt-get install libgraphviz-dev graphviz pkg-config
$ pip install pygraphviz
$ pip install pydotplus
$ pip install django-extensions

demo/settings.py

INSTALLED_APPS = [
          ...........................,
          'django_extensions',
      ]

準備ができたらmanage.pyを使って図を作成できます。

When you are ready, you can use manage.py to create a model diagram.

python manage.py graph_models -a -g -o graph-model.png

-aはall-applications、 -gはgroup-models、-oはoutputで、後に「画像名」を付ける。この場合graph-modelとういう名前のpng画像ファイルを出力せよといった意味になる。

-a means all-applications, -g means group-models and -o means output, followed by "Image Name". In this case, means output a png image file named graph-model.


Responsive image

modelの変更をDBへ反映させる必要があるので、makemigrationsとmigrateを行う。

The model changes need to be reflected in the DB, so run makemigrations and migrate.

python manage.py makemigrations
python manage.py migrate

Responsive image

しかし、既にDBに行を登録してある場合は(例えばdemoshop_itemテーブルに商品として幾つか登録してある)オプションでデフォルト値を入力するか、models.pyにデフォルト値の設定をしてから再度makeigrationsを行う必要があります。

However, if you have already registered a row in the DB, you will need to enter a default value in the (For example, some items are registered in the demoshop_item table.) option or set a default value in models.py and then perform makemigrations again.

You are trying to add a non-nullable field 'seller' to item without a default; 
we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
 Select an option: 1
 Please enter the default value now, as valid Python
 The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
 Type 'exit' to exit this prompt
 >>> 1

今回はオプション1を選択して、デフォルト値として数字の1を入力してみました。今回の場合は上手くいきましたが、設定方法や既に登録してある行によって上手く機能しないことがありますので注意してください。

This time I chose option 1 and entered the number 1 as the default value. It worked fine in this case, but be aware that it may not work depending on how you configure it or what lines you have already registered.

dbshellを使えばどのようにDBへ登録されたかを確認できます。

You can use dbshell to see how it was registered in the DB.

~/djangodemo/demo $ python manage.py dbshell
SQLite version 3.22.0 2018-01-22 18:45:57
Enter ".help" for usage hints.
sqlite>
sqlite> select * from demoshop_item;
1|1000.0|1|1|本1
2|3000.0|1|1|玩具1
3|2000.0|1|1|玩具2
4|1500.0|1|1|本2
5|500.0|1|1|マンガ
sqlite> select * from demoshop_seller;
1|5
sqlite> .schema demoshop_item
CREATE TABLE IF NOT EXISTS "demoshop_item" (
  "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, 
  "price" real NOT NULL, 
  "description" text NOT NULL, 
  "seller_id" integer NOT NULL REFERENCES "demoshop_seller" ("id") DEFERRABLE INITIALLY DEFERRED, 
  "title" varchar(30) NOT NULL);
sqlite> .quit
~/djangodemo/demo $

では次にurls.pyの設定をしていきます。

Now we'll set up urls.py.


Responsive image


demoshop/urls.py

from django.urls import path

from . import views

app_name = 'demoshop'

urlpatterns = [
    path('', views.index, name='index'),
    path('home', views.home, name='home'),
    path('signin', views.signin, name='signin'),
    path('signup', views.signup, name='signup'),
    path('signupview', views.signup_view, name='signupview'),
    path('signout', views.signout, name='signout'),
    path('signinuser', views.signin_user, name='signinuser'),
    path('cartview', views.cart_view, name='cartview'),
    path('registrationview', views.registration_view, name='registrationview'),
    path('registeritem', views.register_item, name='registeritem'),
    path('edititemview', views.edititem_view, name='edititemview'),
]
path('registeritem', views.register_item, name='registeritem')

例えば上記のスクリプトなら、[http://xxxxxx/registeritem]にアクセスがあった場合にviews.pyにあるregister_itemを動かします。

register_itemは「商品を登録させる機能」を担っています。

そして、nameに「registeritem」と名前の指定をするとurlで「http://xxxxxx/registeritem」と書く代わりに[application name:registeritem]とテンプレート内に書くことができるようにしています。

For example, the above script runs the register_item function in views.py when [http://xxxxxx/registeritem] is accessed by the client.

If you specify [registeritem] as the name, you can write [application name:registeritem] in the template instead of writing [http://xxxxxx/registeritem] as the url.


Responsive image




② views.py
② views.py



個別のページや機能別の名前のURLにクライアントがアクセスした場合にviews.pyがそのページの表示や機能の処理を担当します。

When a client accesses an individual page or a URL with a function-specific name, views.py is responsible for displaying the page and handling the functionality.


demoshop/views.py

from django.contrib.auth import authenticate, login, logout
from django.shortcuts import render, redirect, reverse
from urllib.parse import urlencode
from django.contrib.auth.models import User, Group
from demoshop.models import (Item, Customer, Seller,
                             Transaction)
from django.http import HttpResponse

# Create your views here.


def index(request):
    if request.method == "GET":
        if request.user.is_authenticated:
            return redirect('demoshop:home')
        else:
            if request.method == "GET":
                items = Item.objects.all()
                return render(request, 'demoshop/index.html',
                              {'items': items})


def home(request):
    if request.method == "GET":
        user = request.user
        if request.user.is_authenticated:
            username = user.username
            if request.user.groups.filter(name="sellers").count() != 0:
                is_seller = True
                items = Item.objects.filter(seller=user.seller.id)
                inventories = []
                for item in items:
                    transactions = Transaction.objects.filter(item=item.id)
                    for transaction in transactions:
                        inventories.append(transaction)
                items = inventories
            else:
                is_seller = False
                items = Item.objects.all()
            context = {'message': f"ようこそ、{username}さん",
                       'items': items, 'isSeller': is_seller}
            return render(request, 'demoshop/home.html', context)
        return redirect('demoshop:index')
    else:
        return HttpResponse(status=500)


def signin(request):
    if request.user.is_authenticated:
        return redirect('demoshop:home')
    return render(request, 'demoshop/signin.html')


def signup_view(request):
    if request.user.is_authenticated:
        return redirect('demoshop:home')
    return render(request, 'demoshop/signupview.html')


def signout(request):
    logout(request)
    return render(request, 'demoshop/signin.html',
                  {'error_message': "サインアウトしました"})


def signin_user(request):
    if request.method == "POST":
        username = request.POST['inputUsername']
        password = request.POST['inputPassword']

        user = authenticate(request,
                            username=username,
                            password=password)

        if user is not None:
            login(request, user)
            return redirect('demoshop:home')
        else:
            return render(request, "demoshop/signin.html",
                          {'error_message':
                           "ユーザー名かパスワードが間違っています!"})
    else:
        return redirect('demoshop:index')


def signup(request):
    if request.method == "POST":
        username = request.POST['inputUsername']
        email = request.POST['inputEmail']
        password = request.POST['inputPassword']
        seller = False

        try:
            if request.POST['seller']:
                seller = True
        except KeyError:
            seller = False
        if username is not None and \
                email is not None and \
                password is not None:
            if User.objects.filter(username=username).exists():
                return render(request, 'demoshop/registration.html',
                              {'error_message':
                               f"このユーザー名({username})\
                                  はすでに使われています"})
            elif User.objects.filter(email=email).exists():
                return render(request,
                              'demoshop/registration.html',
                              {'error_message': f"このメール \
                                  ({email})はすでに使われています"})
            user = User.objects.create_user(username, email, password)

            if seller:
                if Group.objects.filter(name='sellers').exists():
                    seller_group = Group.objects.get(name='sellers')
                else:
                    Group.objects.create(name='sellers').save()
                    seller_group = Group.objects.get(name='sellers')
                seller_group.user_set.add(user)
                Seller.objects.create(user=user).save()
            else:
                Customer.objects.create(user=user).save()
            user.save()
            login(request, user)
        return redirect('demoshop:home')
    else:
        return redirect('demoshop:registrationview')


def cart_view(request):
    if request.method == "GET":
        user = request.user
        if request.user.is_authenticated:
            username = user.username
            if user.groups.filter(name="sellers").count() != 0:
                return redirect('demoshop:home')
            else:
                transactions = Transaction.objects.filter(
                    customer=user.customer.id)
                cart_items = []
                for transaction in transactions:
                    cart_items.append(transaction.item)
                return render(request, 'demoshop/cartview.html', {
                    'message': "商品カゴ",
                    'cart_items': cart_items,
                    'username': username})
        else:
            return redirect('demoshop:index')
    else:
        return HttpResponse(status=500)


def registration_view(request):
    if request.method == "GET":
        if request.user.is_authenticated:
            username = request.user.username
            if request.user.groups.filter(name="sellers").count() != 0:
                check = request.GET.get('check')
                if check:
                    title = request.GET.get('title')
                    description = request.GET.get('description')
                    price = request.GET.get('price')
                    images_form = request.GET.get('imagesForm')
                    context = {'title': title,
                               'description': description,
                               'isSeller': True,
                               'price': price,
                               'imagesForm': images_form,
                               'check': check,
                               'message': "商品は登録されました"}
                    return render(
                        request, 'demoshop/registrationview.html', context)
                else:
                    return render(request, 'demoshop/registrationview.html', {
                        'message': "商品を登録する",
                        'isSeller': True,
                        'username': username})
            else:
                return redirect('demoshop:home')
        else:
            return redirect('demoshop:index')
    else:
        return HttpResponse(status=500)


def register_item(request):
    if request.method == "POST":
        redirect_url = reverse('demoshop:registrationview')

        title = request.POST['inputTitle']
        description = request.POST['inputDescription']
        price = request.POST['inputPrice']

        """
        ユーザーのタイプミスはHTML側で防ぐようになっていますが、
        Python側でも念のためタイプミスを防ぐようにします。
        """
        """
        User typos are prevented by HTML,
        but as a precaution, I also write Python to prevent typos.
        """
        if not title and not description and not price:
            # Please enter all the items to be registered of the product.
            return render(request, 'demoshop/registrationview.html',
                          {'error_message': '商品の登録項目は全て入力してください。'})
        try:
            float_price = float(price)
        except:
            # The price entered is not a number.
            return render(request, 'demoshop/registrationview.html',
                          {'error_message': '値段が数ではありません。'})
        if float_price <= 0:
            # Prices equal to or less than 0 cannot be registered.
            return render(request, 'demoshop/registrationview.html',
                          {'error_message': '0以下の値段は登録できません。'})
        if float_price >= 1000000:
            # Cannot register the price over 1 million yen.
            return render(request, 'demoshop/registrationview.html',
                          {'error_message': '100万円以上の値段は登録できません。'})
        if len(title) > 30:
            # Titles longer than 30 characters cannot be registered.
            length = f"{title}: {len(title)}文字"
            return render(request, 'demoshop/registrationview.html',
                          {'error_message': '30文字を超えるタイトルは登録できません。',
                           'length': length})
        if len(description) > 90:
            # Product descriptions longer than 90 characters cannot be
            # registered.
            length = f"{description}: {len(description)}文字"
            return render(request, 'demoshop/registrationview.html',
                          {'error_message': '90文字を超える商品説明は登録できません。',
                           'length': length})

        check = True

        parameters = urlencode({'title': title, 'description': description,
                                'price': price, 'check': check})

        url = f'{redirect_url}?{parameters}'

        return redirect(url)
    else:
        return render(request, 'demoshop/registrationview.html',
                      {'error_message': 'something wrong'})


def edititem_view(request):
    if request.method == "GET":
        if request.user.is_authenticated:
            username = request.user.username
            if request.user.groups.filter(name="sellers").count() != 0:
                return render(request,
                              'demoshop/edititemview.html',
                              {'message': "商品を編集する",
                               'isSeller': True,
                               'username': username})
            else:
                return redirect('demoshop:home')
        else:
            return redirect('demoshop:index')
    else:
        return HttpResponse(status=500)

Djangoで行っていることの基本はクライアント(Browser)が特定のURLにアクセスしたら(リクエスト)、それをViewを用いて内部で処理して(ModelがDBとやり取りをする)、Templateでそれをクライアント(Browser)に表示できるようにする(レスポンス)ことです。

The basic thing we do in Django is that once a client (Browser) has accessed a particular URL (Request), it can be processed internally using View (Model interacts with DB) and displayed to the client (Browser) using Template (Response).


Responsive image




③ templatesのHTMLファイル
③ HTML files in the templates



最後にHTMLファイルの中身です。

Finally, the contents of the HTML file.


demoshop/templates/demoshop/index.html

{% extends "demoshop/base.html" %}
{% load static %}
{% block maincss %}
<link href="{% static 'demoshop/css/album.css' %}" rel="stylesheet">{% endblock maincss %}

{% block indexcontent %}
{% load humanize %}
<section class="jumbotron text-center">
    <div class="container">
        <h1>デモショップ</h1>
    </div>
</section>

<div class="album py-5 bg-light">
    <div class="container">

        <div class="row">

            {% for item in items %}
            <div class="col-md-4">
                <div class="card mb-4 shadow-sm">
                    <svg class="bd-placeholder-img card-img-top" width="100%" height="225"
                        xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false"
                        role="img" aria-label="Placeholder: Thumbnail">
                        <title>Placeholder</title>
                        <rect width="100%" height="100%" fill="#55595c" /><text x="50%" y="50%" fill="#eceeef"
                            dy=".3em">Thumbnail</text>
                    </svg>
                    <div class="card-body">
                        <p class="card-text">{{ item.description }}</p>
                        <div class="text-center">
                            <button type="button"
                                class="title-btn btn btn-lg btn-outline-secondary">{{ item.title }}</button>
                            <button type="button"
                                class="price-btn btn btn-lg btn-outline-secondary">¥{{ item.price|floatformat|intcomma }}</button>
                        </div>
                    </div>
                </div>
            </div>
            {% endfor %}
        </div>
    </div>
</div>

<footer class="text-muted">
    <div class="container">
        <p class="float-right">
            <a href="#">Back to top</a>
        </p>
        <p>&copy; デモショップ 2020</p>
    </div>
</footer>

{% endblock indexcontent %}

demoshop/templates/demoshop/home.html

{% extends "demoshop/base.html" %}
{% load static %}
{% block maincss %}
<link href="{% static 'demoshop/css/album.css' %}" rel="stylesheet">{% endblock maincss %}

{% block homecontent %}
{% load humanize %}
<section class="jumbotron text-center">
    {% if isSeller %}
    <div class="container">
        <h2>{{ message }}<br><span style="color: coral;">(Seller)</span></h2>
        <br>
        <h1>Inventory</h1>
    </div>
    {% else %}
    <div class="container">
        <h2>{{ message }}</h2>
    </div>
    {% endif %}
</section>

<div class="album py-5 bg-light">
    <div class="container">

        <div class="row">

            {% for item in items %}
            <div class="col-md-4">
                <div class="card mb-4 shadow-sm">
                    <svg class="bd-placeholder-img card-img-top" width="100%" height="225"
                        xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false"
                        role="img" aria-label="Placeholder: Thumbnail">
                        <title>Placeholder</title>
                        <rect width="100%" height="100%" fill="#55595c" /><text x="50%" y="50%" fill="#eceeef"
                            dy=".3em">Thumbnail</text>
                    </svg>
                    <div class="card-body">
                        <p class="card-text">{{ item.description }}</p>
                        <div class="text-center">
                            <button type="button"
                                class="title-btn btn btn-lg btn-outline-secondary">{{ item.title }}</button>
                            <button type="button"
                                class="price-btn btn btn-lg btn-outline-secondary">¥{{ item.price|floatformat|intcomma }}</button>
                        </div>
                    </div>
                </div>
            </div>
            {% endfor %}
        </div>
    </div>
</div>

<footer class="text-muted">
    <div class="container">
        <p class="float-right">
            <a href="#">Back to top</a>
        </p>
        <p>&copy; デモショップ 2020</p>
    </div>
</footer>

{% endblock homecontent %}
      

demoshop/templates/demoshop/base.html

<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="">
  <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
  <meta name="generator" content="Jekyll v4.0.1">
  <title>デモショップ</title>
  {% load static %}
  <!-- Bootstrap core CSS -->
  <link href="{% static 'demoshop/css/bootstrap.css' %}" rel="stylesheet">

  <style>
    .bd-placeholder-img {
      font-size: 1.125rem;
      text-anchor: middle;
      -webkit-user-select: none;
      -moz-user-select: none;
      -ms-user-select: none;
      user-select: none;
    }

    @media (min-width: 768px) {
      .bd-placeholder-img-lg {
        font-size: 3.5rem;
      }
    }
  </style>
  <!-- Custom styles for this template -->
  {% block maincss %}{% endblock maincss %}
  {% block signincss %}{% endblock signincss %}

</head>

<body>
  <header>
    <div class="collapse bg-dark" id="navbarHeader">
      <div class="container">
        <div class="row">
          <div class="col-sm-8 col-md-7 py-4">
            <h4 class="text-white">デモショップ</h4>
          </div>
          <div class="col-sm-4 offset-md-1 py-4">
            <h4 class="text-white">Menu</h4>
            <ul class="list-unstyled">

              {% if request.path == "/signin" %}
              <li><a href="{% url 'demoshop:signupview' %}" class="text-white">Sign up</a></li>
              {% elif request.path == "/home" %}
              <li><a href="{% url 'demoshop:signout' %}" class="text-white">Sign out</a></li>
              {% if isSeller %}
              <li><a href="{% url 'demoshop:registrationview' %}" style="color: coral;">Register Your
                  Product</a></li>
              <li><a href="{% url 'demoshop:edititemview' %}" style="color: coral;">Update or Delete</a></li>
              {% else %}
              <li><a href="{% url 'demoshop:cartview' %}" class="text-white">Cart</a></li>
              {% endif %}
              {% elif request.path == "/signout" %}
              <li><a href="{% url 'demoshop:signin' %}" class="text-white">Sign in</a></li>
              <li><a href="{% url 'demoshop:index' %}" class="text-white">shop</a></li>
              {% elif request.path == "/editview" or request.path == "/cartview" or request.path == "/registration" %}
              <li><a href="{% url 'demoshop:signout' %}" class="text-white">Sign out</a></li>
              <li><a href="{% url 'demoshop:home' %}" class="text-white">Home</a></li>
              {% elif 'registrationview' in request.get_full_path or 'edititemview' in request.get_full_path and isSeller %}
              <li><a href="{% url 'demoshop:signout' %}" class="text-white">Sign out</a></li>
              <li><a href="{% url 'demoshop:registrationview' %}" style="color: coral;">Register Your
                  Product</a></li>
              <li><a href="{% url 'demoshop:edititemview' %}" style="color: coral;">Update or Delete</a></li>
              {% else %}
              <li><a href="{% url 'demoshop:signupview' %}" class="text-white">Sign up</a></li>
              <li><a href="{% url 'demoshop:signin' %}" class="text-white">Sign in</a></li>
              {% endif %}

            </ul>
          </div>
        </div>
      </div>
    </div>
    <div class="navbar navbar-dark bg-dark shadow-sm">
      <div class="container d-flex justify-content-between">
        <a href="{% url 'demoshop:index' %}" class="navbar-brand d-flex align-items-center">
          <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" stroke="currentColor"
            stroke-linecap="round" stroke-linejoin="round" stroke-width="2" aria-hidden="true" class="mr-2"
            viewBox="0 0 24 24" focusable="false">
            <path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z" />
            <circle cx="12" cy="13" r="4" /></svg>
          <strong>デモショップ</strong>
        </a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarHeader"
          aria-controls="navbarHeader" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
      </div>
    </div>
  </header>

  <main role="main">
    {% block indexcontent %}{% endblock indexcontent %}
    {% block signincontent %}{% endblock signincontent %}
    {% block signupviewcontent %}{% endblock signupviewcontent %}
    {% block homecontent %}{% endblock homecontent %}
    {% block cartviewcontent %}{% endblock cartviewcontent %}
    {% block edititemviewcontent %}{% endblock edititemviewcontent %}
    {% block registrationviewcontent %}{% endblock registrationviewcontent %}
  </main>


  <script src="{% static 'demoshop/js/jquery-3.5.1.slim.min.js' %}"></script>
  <script src="{% static 'demoshop/js/bootstrap.bundle.js' %}"></script>

</body>

</html>

demoshop/templates/demoshop/cartview.html

{% extends "demoshop/base.html" %}
{% load static %}
{% block maincss %}
<link href="{% static 'demoshop/css/album.css' %}" rel="stylesheet">{% endblock maincss %}

{% block cartviewcontent %}
{% load humanize %}
<section class="jumbotron text-center">
    <div class="container">
        <h1>{{ message }}</h1>
    </div>
</section>

<div class="album py-5 bg-light">
    <div class="container">

        <div class="row">

            {% for item in cart_items %}
            <div class="col-md-4">
                <div class="card mb-4 shadow-sm">
                    <svg class="bd-placeholder-img card-img-top" width="100%" height="225"
                        xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false"
                        role="img" aria-label="Placeholder: Thumbnail">
                        <title>Placeholder</title>
                        <rect width="100%" height="100%" fill="#55595c" /><text x="50%" y="50%" fill="#eceeef"
                            dy=".3em">Thumbnail</text>
                    </svg>
                    <div class="card-body">
                        <p class="card-text">{{ item.description }}</p>
                        <div class="text-center">
                            <button type="button"
                                class="title-btn btn btn-lg btn-outline-secondary">{{ item.title }}</button>
                            <button type="button"
                                class="price-btn btn btn-lg btn-outline-secondary">¥{{ item.price|floatformat|intcomma }}</button>
                        </div>
                    </div>
                </div>
            </div>
            {% endfor %}
        </div>
    </div>
</div>

<footer class="text-muted">
    <div class="container">
        <p class="float-right">
            <a href="#">Back to top</a>
        </p>
        <p>&copy; デモショップ 2020</p>
    </div>
</footer>

{% endblock cartviewcontent %}

demoshop/templates/demoshop/edititemview.html

{% extends "demoshop/base.html" %}
{% load static %}
{% block maincss %}
<link href="{% static 'demoshop/css/album.css' %}" rel="stylesheet">{% endblock maincss %}

{% block edititemviewcontent %}
{% load humanize %}
<section class="jumbotron text-center">
    <div class="container">
        <h1>{{ message }}</h1>
    </div>
</section>


<footer class="text-muted">
    <div class="container">
        <p class="float-right">
            <a href="#">Back to top</a>
        </p>
        <p>&copy; デモショップ 2020</p>
    </div>
</footer>

{% endblock edititemviewcontent %}

demoshop/templates/demoshop/registrationview.html

{% extends "demoshop/base.html" %}
{% load static %}
{% block maincss %}
<link href="{% static 'demoshop/css/album.css' %}" rel="stylesheet">{% endblock maincss %}

{% block registrationviewcontent %}
{% load humanize %}
<section class="jumbotron text-center">
    <div class="container">
        <h1>{{ message }}</h1>
        <h1 class="custum-signin-h1 h3 mb-3 font-weight-normal" style="color: red;">{{ error_message }}</h1>
        <p style="color: red;">{{ length }}</p>
    </div>
</section>

{% if check %}
<div class="album py-5 bg-light">
    <div class="container">
        <div class="row">
            <div class="col-md-4">
                <div class="card mb-4 shadow-sm">
                    <svg class="bd-placeholder-img card-img-top" width="100%" height="225"
                        xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false"
                        role="img" aria-label="Placeholder: Thumbnail">
                        <title>Placeholder</title>
                        <rect width="100%" height="100%" fill="#55595c" /><text x="50%" y="50%" fill="#eceeef"
                            dy=".3em">{{ filename }}</text>
                    </svg>
                    <div class="card-body">
                        <p class="card-text">{{ description }}</p>
                        <div class="text-center">
                            <button type="button"
                                class="title-btn btn btn-lg btn-outline-secondary">{{ title }}</button>
                            <button type="button"
                                class="price-btn btn btn-lg btn-outline-secondary">¥{{ price|floatformat|intcomma }}</button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
{% else %}
<div class="text-center">
    <h1 class="custum-signin-h1 h3 mb-3 font-weight-normal">Register Your Product</h1>
    <form action="{% url 'demoshop:registeritem' %}" method="post" enctype="multipart/form-data">
        {% csrf_token %}
        <div class="form-group" style="margin: 1rem 3rem;">
            <label for="inputTitle" class="sr-only">Title</label>
            <input type="text" name="inputTitle" id="inputTitle" class="form-control" placeholder="Title" required
                autofocus>
        </div>
        <div class="form-group" style="margin: 1rem 3rem;">
            <label for="inputDescription" class="sr-only">Description</label>
            <textarea name="inputDescription" id="inputDescription" class="form-control" placeholder="Description"
                rows="3" required autofocus></textarea>
        </div>
        <div class="form-group" style="margin: 1rem 3rem;">
            <label for="inputEmail" class="sr-only">Price</label>
            <input type="number" name="inputPrice" id="inputPrice" class="form-control" placeholder="Price" required
                autofocus>
        </div>

        <div class="form-group" style="margin: 1rem 3rem;">
            <button class="btn btn-primary btn-block" type="submit" value="Edit">Save</button>
        </div>
    </form>
</div>
{% endif %}

<footer class="text-muted">
    <div class="container">
        <p class="float-right">
            <a href="#">Back to top</a>
        </p>
        <p>&copy; デモショップ 2020</p>
    </div>
</footer>

{% endblock registrationviewcontent %}

demoshop/templates/demoshop/signupview.html

{% extends "demoshop/base.html" %}
{% load static %}
<!-- Custom styles for this template -->
{% block signincss %}
<link href="{% static 'demoshop/css/signin.css' %}" rel="stylesheet">{% endblock signincss %}

{% block signupviewcontent %}

<div class="text-center">
  {% if error_message %}
  <h1 class="custum-signin-h1 h3 mb-3 font-weight-normal" style="color: red;">{{ error_message }}</h1>
  {% else %}
  <h1 class="custum-signin-h1 h3 mb-3 font-weight-normal">Sign-up Example</h1>
  {% endif %}
  <form class="form-signin" action="{% url 'demoshop:signup' %}" method="post">
    {% csrf_token %}
    <label for="inputUsername" class="sr-only">Username</label>
    <input type="text" name="inputUsername" id="inputUsername" class="form-control" placeholder="Username" required
      autofocus>
    <label for="inputEmail" class="sr-only">Email address</label>
    <input type="email" name="inputEmail" id="inputEmail" class="form-control" placeholder="Email address" required
      autofocus>
    <label for="inputPassword" class="sr-only">Password</label>
    <input type="password" name="inputPassword" id="inputPassword" class="form-control" placeholder="Password" required>
    <div class="checkbox mb-3">
      <label>
        <input type="checkbox" id="seller_checkbox" name="seller" value="seller"><span
          style="color: red; margin-left : 10px;">Sign up as
          a seller</span>
      </label>
    </div>
    <button class="btn btn-lg btn-primary btn-block" type="submit" value="Signup">Sign up</button>
    <p class="mt-5 mb-3 text-muted">&copy; デモショップ 2020</p>
  </form>
</div>

{% endblock signupviewcontent %}

demoshop/templates/demoshop/signin.html

{% extends "demoshop/base.html" %}
{% load static %}
<!-- Custom styles for this template -->
{% block signincss %}
<link href="{% static 'demoshop/css/signin.css' %}" rel="stylesheet">{% endblock signincss %}

{% block signincontent %}
<div class="text-center">
  {% if error_message %}
  <h1 class="custum-signin-h1 h3 mb-3 font-weight-normal" style="color: red;">{{ error_message }}</h1>
  {% else %}
  <h1 class="custum-signin-h1 h3 mb-3 font-weight-normal">Signin Example</h1>
  {% endif %}
  <form class="form-signin" action="{% url 'demoshop:signinuser' %}" method="post">
    {% csrf_token %}
    <label for="inputUsername" class="sr-only">Username</label>
    <input type="text" name="inputUsername" id="inputUsername" class="form-control" placeholder="Username" required
      autofocus>
    <label for="inputPassword" class="sr-only">Password</label>
    <input type="password" name="inputPassword" id="inputPassword" class="form-control" placeholder="Password" required>
    <button class="btn btn-lg btn-primary btn-block" type="submit" value="Signin">Sign in</button>
    <p class="mt-5 mb-3 text-muted">&copy; デモショップ 2020</p>
  </form>
</div>
{% endblock signincontent %}


以上です。お疲れ様です。

That's all. Thank you for your hard work.





See You Next Page!