Published Date : 2020年6月15日11:28
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 |
② views.py ② views.py |
③ templatesのHTMLファイル ③ HTML files in the templates |
ページの最後へ Go to the end of the page. |
では取引用のモデルの追加と、出品者ユーザーと出品物の紐づけを行っていきます。
Then, We will add a model for transaction and link the seller user and the item.
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
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.
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
しかし、既に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.
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.
個別のページや機能別の名前の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.
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).
最後にHTMLファイルの中身です。
Finally, the contents of the HTML file.
{% 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>© デモショップ 2020</p> </div> </footer> {% endblock indexcontent %}
{% 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>© デモショップ 2020</p> </div> </footer> {% endblock homecontent %}
<!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>
{% 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>© デモショップ 2020</p> </div> </footer> {% endblock cartviewcontent %}
{% 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>© デモショップ 2020</p> </div> </footer> {% endblock edititemviewcontent %}
{% 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>© デモショップ 2020</p> </div> </footer> {% endblock registrationviewcontent %}
{% 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">© デモショップ 2020</p> </form> </div> {% endblock signupviewcontent %}
{% 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">© デモショップ 2020</p> </form> </div> {% endblock signincontent %}
以上です。お疲れ様です。
That's all. Thank you for your hard work.