見出し画像

djangoで作る簡易ECサイト Part1

djangoで簡単なTodoアプリ等は作成できたけれども、初心者から中級者への道のりが見えない。オリジナルWebアプリを作る前に、もう少しだけ色々なアプリを作って実践練習をしたい方を対象としています。

最近は、djangoの書籍も増えてきていますがECサイトアプリを扱った書籍はまだ見かけません。Webアプリを学んだら、いつかはECサイトを作ってみたいものです。TodoアプリやSNSアプリに比べてサンプルが少ないので、少しだけハードルが高いかもしれませんがアプリの全体像をイメージしながら学習する事により、オリジナルアプリ作成時の参考になるように記事を作成していきます。

Part1では、⓪ECサイトのワイヤーフレーム作成と必要なテーブルの検討、①ECサイトプロジェクトの開始とアプリの作成、②allauthによる認証機能の実装とECサイト用カスタマイズまでを説明いたします。

目標

本アプリの作成を通じて以下の機能を実装できるようになります。クラスベース汎用ビューを前提として開発を進めます。

・簡易ECサイトの作成(決済機能は、付けておりません)
・ショッピングカート機能の実装

開発環境

・Python 3.9.4
・django 3.2.4
・Mac M1 Big Sur
・Visual Studio Code(Pycharmでも構いません)
・ブラウザはSafari(もしくはChrome)
・miniforge(ARMアーキテクチャに最適化されているため)
↑は、Windows環境であればAnacondaでも構いません。

前提条件

・Pythonの文法がわかる。
・djangoで簡単なWebアプリを作った経験がある。
・HTML,CSS,Bootstrapの基本を理解している。
・Webアプリ開発用の環境構築が完了しており、必要なパッケージをインストール出来る事。
・allauthを使ってでカスタムユーザーが作成できること(こちらを参照)。

ECサイトの全体像

ECサイトには、1. accounts(allauthによる認証関連機能) と2. ecsite(ECサイト関連機能)の2つのアプリを作成します。以下の画像でイメージを掴んで下さい。

パティスリーNS2

開発の手順

 以下の手順でECサイトの開発を進める事とします。⓪でECサイトの構想を検討してから、具体的なECサイトの開発に入ります。

⓪ECサイトのワイヤーフレームと必要なテーブルの検討
①ECサイトプロジェクトの開始とアプリの作成
②allauthによる認証機能の実装とECサイト用カスタマイズ
③ECサイトのモデル作成管理画面から商品登録
④トップページ、商品一覧と商品詳細ページの作成
⑤カート機能実装と関連ページ作成
⑥チェックアウト画面とPDFによる領収書作成

仮想環境と必要なパッケージ

django_env(=python3.9.4)という仮想環境を作成し、以下のパッケージをインストールします。インストール後のパッケージ一覧は画像を参照下さい。

・django_envという仮想環境を作成し
・django3.2.4
・Pillow (8.2.0)
・django-bootstrap4(1.1.1)
・bcrypt (3.20.)
・beautifulsoup4(4.9.3)
・allauth (0.41.0)
・reportlab (3.5.68)

⓪ECサイトのワイヤーフレームと必要なテーブルの検討

djangoのプロジェクトを作成する前にECサイトのワイヤーフレームを作成して、必要な機能やテーブルを検討します。TodoアプリやSNSアプリなどは様々な書籍で紹介されていますので、必要なテーブルもイメージが付きやすいと思います。しかし、ECサイトについて必要なテーブルが網羅的に記載されているサイトは多くないためイメージがつかないかもしれません。ECサイトに必要な機能をワイヤーフレームを作成しながら検討していきます。必要な機能が抽出出来たら、必要なテーブルをER図を作成して検討していきます。

 ECサイトのワイヤーフレーム作成

まずは、ECサイトで何を扱うかを決めます。自分の興味のあるサイトが良いでしょう。私はケーキが好きですので、パティスリーNSというケーキ屋さんを題材とすることとします。題材が決まったらワイヤーフレームの作成に入ります。ワイヤーフレーム作成のツールもいろいろありますが、ノートに手書きでも構わないと思います。私は、NotabilityというアプリでiPadにワイヤーフレームを作成しましたが、見栄えが悪いので実際の画面をもとに簡単に解説していきます。
 最低限必要なページは、1.トップページ、2.商品一覧ページ、3.商品詳細ページ、4.ショッピングカートページ、5.チェックアウトページの5つかなと思います。この段階では、大まかなところを決めるのが良いと思います。ER図や実際にアプリを作りながら、必要な機能が追加になったり等もありますので完璧に作りこまずに先に進みましょう。
 今回は、認証周りはallauthを使いますのでログインログアウトページなどはちょっとした修正だけになりますのでカウントしていません。

トップページ

画像3

商品一覧ページ

商品一覧

商品詳細ページ

画像4

ショッピングカートのページ

画像5

チェックアウトページ

チェックアウト

 ECサイトに必要なテーブルをER図で検討

ECサイトに限らずWebアプリを作成する際は似たようなWebアプリをベンチマークすると良いでしょう。ECサイトは星の数ほどありますので、自分のお気に入りのサイトをいくつかベンチマークしてER図作成の参考とします。
 まず初めにECサイトに登場するEntity=モノを洗い出して、エンティティとして定義しましょう。エンティティは、マスターテーブルとなる「リソースエンティティ」とトランザクションテーブルとなる「イベントエンティティ」のふたつがあります。その両面で洗い出しを行うと検討モレを防げます。エンティティといっても、分かりにくいのでマスターテーブルトランザクションテーブルをそれぞれ検討していきます。
 ECサイトのマスターテーブルは、「ユーザー」テーブル、「商品」テーブル、「商品画像」テーブルが最低限必要そうです。トランザクションテーブルですが、受注した合計金額と購入者を管理する「注文」テーブル、注文した商品、数量と購入したユーザー情報を管理する「注文明細」テーブル、そして、ショッピングカート用の「カートアイテム」テーブルが必要になります。ショップによっては、複数の配送先を登録出来たり、Wishリストなど他にもいろいろな機能がありますが、今回は最低限なECサイトとしますでそれらは実装しません。原価なども管理したいのであれば、商品テーブルと関連付けた原価テーブルなどを作成してもよいでしょう。必要なテーブルが抽出出来たら、それぞれに必要なカラムを検討してそれぞれのリレーションを検討します。
 今回作成した最終的なER図は、以下の画像となります。Pycharmには、ER図を書き出してくれる便利な機能が備わっていますのでそちらを使っています(customuserテーブルのavatorは今回使わなかったため、消しています)。

ER図

 allauthを使い認証周りを作成していきます。カスタムユーザーを別途accounts/models.pyで定義します。カスタムユーザーテーブル=customuserテーブルには、氏名=fullname、郵便番号=zipcode住所=address電話番号=phoneなどのカラムを追加します。 
 次は、商品テーブル=productsを検討しす。商品の名前=name種類=type価格=price在庫数=stock商品説明=comments原材料= ingredientsサイズ=sizeキャンペーン情報=campaignなどを登録できるようにします。
 商品には写真が必要ですので、商品画像=product_picturesテーブルも作成します。商品画像は、商品に紐づきますので商品ID=product_idを外部キーに持ち、画像=picture画像の優先順位=priorityを登録できるようにしましょう。優先順位ですが、ページで大きく表示させたい画像などに若い順番を付けていきましょう。以下がリレーションのイメージ図になります。

商品と画像のリレーション

 次はカートアイテムテーブル=cart_itemsです。少し分かりにくいと思いますので、下の画像を見ながら理解してください。カートアイテムテーブルは、商品ID =product_idユーザーID =user_idの外部キーを持ちます。下の画像では、カートにはモンブラン(product_id=1) と誕生日用ローソク(product_id=3)が入れられている事がproduct_idから分かります。また、user_idでそのカートの持ち主を表しますので、カートの持ち主が東京 花子であることが分かります。それぞれのカートアイテムレコードには、商品数量=qtyも登録できるようにしています。カートテーブルにユーザーID、商品IDを紐づけて数量を登録できるようにすることで、ユーザー毎のショッピングカートにどの商品が、いくつ入っているかを表す事が出来ます。 

カートのリレーション

 注文テーブル=orderは、注文の合計金額=total_priceと注文とユーザーを紐づけするためuser_idを外部キーとして登録します。
 最後に注文明細テーブル=order_itemsを登録します。注文明細には、ユーザー情報、ユーザー情報および数量が必要です。カラムには、ユーザー情報customuser_id商品ID= product_idを外部キーとして持たせましょう。注文数量=qryを登録できるようにさせればすべてのテーブルは完了です。

①ECサイトプロジェクトの開始とアプリの作成

ECサイトプロジェクトを開始します。プロジェクトの保存先フォルダーを作成して、ターミナルで所定のフォルダーまで移動します。djangoがインストールされている仮想環境をアクティベートした後に、以下のコマンドでプロジェクトを開始します。

% django-admin startproject config .

その中にaccountsアプリとecsiteアプリを作成します。ターミナルからフォルダーに移動し、まずはaccountsアプリを作成していきましょう。ecsiteアプリは後ほど作成します。

python manage.py startapp accounts

これから必要となるフォルダーやファイルを最初に作ります。画像を確認して下さい。特に気をつけていただきたいのは、templates内のaccountフォルダです。accountsではなく、accountです。allauthlogin.htmlなどを上書きする予定ですので、わざとaccountにしています。

最初のフォルダ体系

 設定ファイル(settings.py)の編集

これまでに作成したアプリ、パッケージ、フォルダーなどが使えるようにsettings.pyを変更していきます。settings.pyのコードは以下のようになります。細かい説明は、djangoで作る本格的なSNSアプリケーション 番外編 allauthによる認証を参照ください。

config/settings.py

from pathlib import Path
import os  # 追加
from django.contrib.messages import constants as messages  # 追加

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-dar0kg&&a6g6cu85$ot#c!la'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
   'django.contrib.admin',
   'django.contrib.auth',
   'django.contrib.contenttypes',
   'django.contrib.sessions',
   'django.contrib.messages',
   'django.contrib.staticfiles',
   'accounts',  # 追加
   'bootstrap4',  # 追加
   'django.contrib.sites',  # 追加
   'allauth',  # 追加
   'allauth.account',  # 追加
   'allauth.socialaccount',  # 追加
]

SITE_ID = 1 # 追加
LOGIN_REDIRECT_URL = '/' # 追加
ACCOUNT_LOGOUT_REDIRECT_URL = 'account_login' # 追加
ACCOUNT_EMAIL_VERIFICATION = 'none' # 追加

# Bootstrap4 jqueryを使用するため追加
BOOTSTRAP4 = {
   'include_jquery': True,
}


MIDDLEWARE = [
   'django.middleware.security.SecurityMiddleware',
   'django.contrib.sessions.middleware.SessionMiddleware',
   'django.middleware.common.CommonMiddleware',
   'django.middleware.csrf.CsrfViewMiddleware',
   'django.contrib.auth.middleware.AuthenticationMiddleware',
   'django.contrib.messages.middleware.MessageMiddleware',
   'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'config.urls'

TEMPLATES = [
   {
       'BACKEND': 'django.template.backends.django.DjangoTemplates',
       'DIRS': [BASE_DIR / 'templates'], # 追加
       'APP_DIRS': True,
       'OPTIONS': {
           'context_processors': [
               'django.template.context_processors.debug',
               'django.template.context_processors.request',
               'django.contrib.auth.context_processors.auth',
               'django.contrib.messages.context_processors.messages',
           ],
           # Bootstrap4を使用するために追加
           'builtins': [
               'bootstrap4.templatetags.bootstrap4', ],
       },
   },
]

WSGI_APPLICATION = 'config.wsgi.application'


# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = {
   'default': {
       'ENGINE': 'django.db.backends.sqlite3',
       'NAME': BASE_DIR / 'db.sqlite3',
   }
}


# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
   {
       'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
   },
   {
       'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
   },
   {
       'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
   },
   {
       'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
   },
]


# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'ja'  # 変更

TIME_ZONE = 'Asia/Tokyo'  # 変更

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

STATIC_URL = '/static/'
# 追加
STATICFILES_DIRS = (
   os.path.join(BASE_DIR, "static"),
)
# 追加 mediaを扱うための設定
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 追加
IMAGE_URL = '/images/'
IMAGE_ROOT = os.path.join(BASE_DIR, 'images')

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

# 追加 メッセージタグの設定
MESSAGE_TAGS = {
   messages.ERROR: 'alert alert-danger',
   messages.WARNING: 'alert alert-warning',
   messages.SUCCESS: 'alert alert-success',
   messages.INFO: 'alert alert-info'
}

# allauth 関連追加
AUTH_USER_MODEL = 'accounts.CustomUser'
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_LOGOUT_ON_GET = True

 CustomUserモデル作成とマイグレーション

これからカスタムユーザーを作ってマイグレーションを実施します。CustomUserteモデルには、氏名=fullname、郵便番号=zipcode、住所=address、電話番号=phoneなどのカラムを追加します。accounts/ models.pyを開いて実装していきましょう。最終的なコードは以下になります。

accounts/ models.py

from django.db import models
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import UserManager, PermissionsMixin

class UserManager(UserManager):
   def _create_user(self, email, password, **extra_fields):
       email = self.normalize_email(email)
       user = self.model(email=email, **extra_fields)
       user.set_password(password)
       user.save(using=self._db)
       return user
   def create_user(self, email, password=None, **extra_fields):
       extra_fields.setdefault('is_staff', False)
       extra_fields.setdefault('is_superuser', False)
       return self._create_user(email, password, **extra_fields)
   def create_superuser(self, email, password, **extra_fields):
       extra_fields.setdefault('is_staff', True)
       extra_fields.setdefault('is_superuser', True)
       if extra_fields.get('is_staff') is not True:
           raise ValueError('Superuser must have is_staff=True.')
       if extra_fields.get('is_superuser') is not True:
           raise ValueError('Superuser must have is_superuser=True.')
       return self._create_user(email, password, **extra_fields)

class CustomUser(AbstractBaseUser, PermissionsMixin):
   email = models.EmailField('メールアドレス', unique=True)
   fullname = models.CharField('氏名', max_length=50)    
   zipcode=models.CharField(max_length=8, blank=True, null=True)
   address=models.CharField(max_length=100, blank=True, null=True)
   phone=models.CharField(max_length=11, blank=True, null=True)
   is_active = models.BooleanField(default=True)
   is_staff = models.BooleanField(default=False)
   objects = UserManager()
   USERNAME_FIELD = 'email'
   EMAIL_FIELD = 'email'
   REQUIRED_FIELDS = []
   class Meta:
       verbose_name = ('user')
       verbose_name_plural = ('users')
   def clean(self):
       super().clean()
       self.email = self.__class__.objects.normalize_email(self.email)

ユーザーモデルが作成できましたので、マイグレーションをおこないます。以下の画像のようにデータベースが作成されていれば成功です。もし、マイグレーションでエラーが発生してしまった場合は、これ以前に一度マイグレーションしていないか思い出してみて下さい。モデル定義前にマイグレーションをかけてしまうと解決が難しいので、素直にもう一度プロジェクトを作成し直した方が時短になります。

python manage.py makemigrations
python manage.py migrate 

画像12

②allauthによる認証機能の実装とECサイト用カスタマイズ

 allauthによる認証機能の実装

allauthにログイン、ログアウト、サインイン等の基本的な機能を備えています。このため、パスを通して見た目を少し整えます。config/urls.pyとaccounts/urls.pyを開きパスを通します。config/urls.pyにはallauthaccountsurlsが使えるように設定し、accounts/urls.pyにもパスを通します。

config/urls.py

from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static  # mediaを使うために追加
from . import settings  # mediaを使うために追加
urlpatterns = [
   path('admin/', admin.site.urls),
   path('accounts/', include('allauth.urls')),  # 追加
   path('accounts/', include('accounts.urls')),  # 追加
]

if settings.DEBUG:
   urlpatterns += static(settings.IMAGE_URL, document_root=settings.IMAGE_ROOT)  
   urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)   

accounts/urls.py

from django.urls import path
from .views import HomeView
app_name = 'accounts'
urlpatterns = [
   
]

 allauthのECサイト用カスタマイズ

allauth標準のテンプレートは味気がありませんので、自分好みのテンプレートを作成して上書きしていきます。まずは、base.htmlを作成します。下記の画像から出来上がりのイメージを掴んでください。

ログイン画面

ログイン画面

サインアップ画面

サインイン画面

templates/base.htmlのコードは、以下のようになります。bootstrapやstaticフォルダー中のCSS読み込みをbase.htmlで実施しています。また、bootstrapのnavbarもここで設定しています。

templates/base.html

{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
   <title>Patisserie NS</title>
   {# bootstrapのCSS、自作のCSSを読み込む#}
   <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
   integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
   <link rel="stylesheet" href="{% static 'style.css' %}">
   {% load bootstrap4 %}
</head>
<body>
<header>
   <nav class="navbar navbar-expand-sm navbar-dark bg-dark mb-3">
       <a class="navbar-brand text-warning" href="#">Patisserie NS</a>
       <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText"
               aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
           <span class="navbar-toggler-icon"></span>
       </button>
       <div class="collapse navbar-collapse justify-content-end" id="navbarText">
           <ul class="navbar-nav ">
               <li class="nav-item">
                   <a class="nav-link text-warning" href="#">
                       ホーム
                   </a>
               </li>
               <li class="nav-item">
                   <a class="nav-link text-warning" href="#">
                       商品一覧
                   </a>
               </li>{% if user.is_authenticated %}
               <li class="nav-item">
                   <a class="nav-link text-warning" href="#">
                       カートを見る<i class="fas fa-shopping-cart"></i>
                   </a>
               </li>
               <li class="nav-item">{# {% url 'account_logout' %}は、Allauthの定型文 #}
                   <a class="nav-link text-warning" href="{% url 'account_logout' %}">Logout</a>
               </li>{% else %}
               <li class="nav-item">{# {% url 'account_login' %}は、Allauthの定型文 #}
                   <a class="nav-link text-warning" href="{% url 'account_login' %}">
                       Login
                   </a>
               </li>
               <li class="nav-item">{# {% url 'account_signup' %}は、Allauthの定型文 #}
                   <a class="nav-link text-warning" href="{% url 'account_signup' %}">
                       Sign in
                   </a>
               </li>{% endif %}
           </ul>
       </div>
   </nav>
</header>
{% block content %}
   <!-- ここに各htmlの内容が反映される -->
{% endblock content %}
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS,Fontawasome -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
       integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
       crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
       integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
       crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
       integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
       crossorigin="anonymous"></script>
<script defer src="https://use.fontawesome.com/releases/v5.7.2/js/all.js"></script>
</body>
</html>

ログイン画面、ユーザー登録画面にもbootstrapを適用していきます。テンプレートを保存するフォルダをtemplate/accountにしましたが、allauthの標準テンプレートを上書きするために設定しました。フォルダ名だけ間違えなければ、シンプルなページですのでコードのみ載せます。

templates/account/login.html

{% extends 'base.html' %}
{% block content %}
   <div class="row">
       <div class="col-sm-6 offset-sm-3">
           <h1 >ログイン</h1>
           {#  ここ{% url 'account_login' %}は、Allauthを使う際の定型文#}
           <form method="POST" action="{% url 'account_login' %}">
               {% csrf_token %}
               {% bootstrap_form form %}
               <button type="submit" class="btn btn-primary" name="button">
                   ログイン
               </button>
           </form>
       </div>
   </div>
{% endblock %}

templates/account/signup.html

{% extends 'base.html' %}
{% block content %}
   <div class="row">
       <div class="col-sm-6 offset-sm-3">
           <h1 class="mb-4">ユーザー登録</h1>
           {#  ここ{% url 'account_signup' %}は、Allauthを使う際の定型文#}
           <form method="POST" action="{% url 'account_signup' %}">
               {% csrf_token %}
               {% bootstrap_form form %}
               <button type="submit" class="btn btn-primary" name="button">
                   登録
               </button>
           </form>
       </div>
   </div>
{% endblock %}

ターミナルからサーバーを立ち上げhttp://127.0.0.1:8000/accounts/signup/へアクセスして動作を確認して見て下さい。以下のようにbase.htmlおよびbootstrapが適用されているでしょうか?

画像13

今回はカスタムユーザーモデルに住所情報を登録・更新できるようにしています。allauthでは、標準で準備されていない部分ですので自分で作成する必要があります。ユーザー情報の更新となりますので、CRUDのU=Updateということでクラスベース汎用ビューのUpdateViewを使っていきます。accounts/views.pyを開いて下さい。
 コードの前半で必要なモジュールをインポートします。UpdateView、 LoginRequiredMixin及びプロフィール更新に必要なフォームProfileEditForm(後ほど作成します)をインポートします。住所登録変更画面のテンプレートは、edit_profile.htmlとし、使用するモデルはCustomUser、使用するフォームはProfileEditFormになります。更新完了後は、success_url= '/accounts /edit_profile/'として同じページを再度読み込みます。

accounts/views.py

from django.views.generic.edit import UpdateView  # 追加
from django.contrib.auth.mixins import LoginRequiredMixin  # 追加
from .models import CustomUser # 追加
from .forms import ProfileEditForm  # 追加

class ProfileEditView(LoginRequiredMixin, UpdateView):  # 追加
   template_name = 'account/edit_profiles.html'
   model = CustomUser
   form_class = ProfileEditForm
   success_url = '/accounts/edit_profile/'
   def get_object(self):
       return self.request.user

次は、ProfileEditFormaccounts/forms.pyに設定します。ProfileEditFormはforms.ModelFormを継承して作成します。def __init__(self, *args, **kwargs):では、bootstrapのform-controlが出来るように設定します。次に、Class Metaとして、フォームのフィールドを設定します。使用するモデルは、CustomUser、ユーザー情報として更新したいフィールドをfull name, email, address, zipcode, phoneとします。help_texts ={ }では、それぞれのフィールドの補足を加えられます。ここでは、fullnameの補足として氏名と表示されるようにしています。zipcode、address、phoneもそれぞれ郵便番号、住所、電話番号としています。好みで設定を加えてみて下さい。

accounts/forms.py

from django import forms
from .models import CustomUser

class ProfileEditForm(forms.ModelForm):
   def __init__(self, *args, **kwargs):
       super(ProfileEditForm, self).__init__(*args, **kwargs)

       for field in self.fields.values():  # bootstrapで使用するform-controlクラス
           field.widget.attrs['class'] = 'form-control'

   class Meta:
       model = CustomUser
       fields = ('fullname','zipcode', 'address', 'phone')
       help_texts = {
           'fullname':'氏名',
           'zipcode': '郵便番号',
           'address': '住所',
           'phone': '電話番号',
       }

urls.pyでパスを通します。名前空間は、name='edit_profile'と設定します。

accounts/urls.py

from django.urls import path
from .views import ProfileEditView  # 追加

app_name = 'accounts'
urlpatterns = [
   path('edit_profile/', ProfileEditView.as_view(), name='edit_profile'),  
]

対応するテンプレートを作成します。こちらも、他のテンプレートとあまり変わりなくシンプルにしています。

account/edit_profile.html

{% extends 'base.html' %}
{% block content %}
   <div class="content-wrapper">
       <div class="container-fluid">
           <div class="row">
               <div class="col-sm-6 offset-sm-3">
                   <div class="card">
                       <div class="card-header">
                           <h4><b>住所登録・更新</b></h4>
                       </div>
                       <div class="card-body">                            
                           <form action="{% url 'accounts:edit_profile' %}" method="post">
                               {% csrf_token %}
                               {% bootstrap_form form %}
                               <button class='btn btn-outline-success btn-block' type="submit">更新</button>
                           </form>
                       </div>
                   </div>
               </div>
           </div>
       </div>
   </div>
{% endblock %}

ターミナルからサーバーを立ち上げhttp://127.0.0.1:8000/accounts/ edit_profile/へアクセスして動作を確認して見て下さい。LoginRequiredMixinを設定していますので、ログインしていない状態ではアクセスできないと思います。エラーが表示された場合は、サインアップしてから再度アクセスしてみてください。下の画像のように、氏名や住所などの登録更新画面が出ましたでしょうか?

画像13

次のPart2では、③ECサイトのモデル作成と管理画面から商品登録と④トップページと商品詳細ページの作成について説明します。

この記事が気に入ったらサポートをしてみませんか?