LoginSignup
31
41

More than 3 years have passed since last update.

SEIYU風のECサイトを作りましょう(1)要求分析とプロジェクト初期化

Last updated at Posted at 2019-08-18

前回の記事

Django REST framework + Vue.js「SEIYU風のECサイトを作りましょう」全6回(予定)

前書き

何かを開発する場合、依頼者からの要求を満足させる必要があります。
その依頼者とは、お客様や上司、マネージャーなどが該当します。
では、今回のECサイトにはどのような要求があるのでしょうか。

機能要求分析

SEIYUのECサイトを分析して機能を割り出すと、大体以下のものになります。
- 商品展示
- 商品画像
- 特定の商品展示
- 商品検索
- 商品詳細
- ログイン
- 一般ユーザー関連
- 商品レビュー
- 商品をかごに追加できる
- 商品をお気に入りにする
- 出品者と連絡
- 支払い

まだまだあるかもしれませんが、以上の機能さえ満たせば、ECサイトとして機能します。
機能に基づいて、モジュールの構成を図にしてみましょう。

キャプチャ1.PNG

ここまで来ればどんなモジュールを開発する必要があるのかは、明確になりました。

プロジェクト初期化

仮想環境のセットアップ

参考資料
Pythonの仮想環境の作り方

ここで私はvirtualenvwrapperで仮想環境作りますが、virtualenvなどのライブラリ使用でもオーケーです。

mkvirtualenv qiita-Django-supermarket

以下の表示が確認できたら完成です。

(qiita-Django-supermarket) ディレクトリ>

プロジェクト管理

今回のプロジェクトを配置したいディレクトリの配下で、以下のコマンドを実行してください。

 mkdir qiita-Django-supermarket
 cd qiita-Django-supermarket
 touch CHANGELOG.md && touch LICENSE && touch README.md && touch requirements.txt

実行後、ディレクトリ配下は以下のような構成になります。

qiita-Django-supermarket
|-- CHANGELOG.md
|-- LICENSE
|-- README.md
|-- requirements.txt

先ず、この四つのファイル何に使うか説明します。

CHANGELOG.md

バージョンごとにどのような新機能が追加されたか、どのような機能が変更されたのか等を管理し説明するテキストファイルのこと。
Gitで自動生成もできるが基本は手書きを行う。
どんな内容記入すれば良いのか、Djangoのリポジトリ参考すればいいと思います。
https://github.com/django/django/tree/master/docs/releases

LICENSE

もしオープンソースのプロジェクトなら、著作権などの声明を記入することができる。
詳細はwikiを参考してください。
https://ja.wikipedia.org/wiki/オープンソースライセンス
内部のプロジェクトなら、このファイルを作る必要はありません。

README.md

何を記入するかは自由です。
どういった経緯で開発したのか、参加者など、プロジェクトの理解を深めるために存在します。

requirements.txt

使用してるPythonパッケージを記録する為のファイル。
pip freeze >requirements.txtで自動生成もできます。
複数プロジェクト作成し、環境に汚染の可能性がもしあれば、手書きでも良いでしょう。
pip install -r requirements.txtで記録したPythonパッケージでのインストールができます。

ライブラリインストール

今回のアプリに必要なライブラリをインストールしましょう。
公式ドキュメント

pip install django
pip install djangorestframework
pip install coreapi
pip install Markdown
pip install Pygments
pip install django-filter
pip install django-guardian
pip install Pillow

2019年08月17日では、以下のバージョンのライブラリがインストールされました。 :point_up:

certifi             2019.6.16
chardet             3.0.4
coreapi             2.3.3
coreschema          0.0.4
Django              2.2.4
django-filter       2.2.0
django-guardian     2.0.0
djangorestframework 3.10.2
idna                2.8
itypes              1.1.0
Jinja2              2.10.1
Markdown            3.1.1
MarkupSafe          1.1.1
Pillow              6.1.0
pip                 19.2.2
Pygments            2.4.2
pytz                2019.2
requests            2.22.0
setuptools          41.1.0
sqlparse            0.3.0
uritemplate         3.0.0
urllib3             1.25.3
wheel               0.33.4

スタートプロジェクト

qiita-Django-supermarketディレクトリの配下で以下コマンドを実行します。
今回のDRFはAPIを提供するのがメインの役割なので、apiと命名します。
PyCharmで作ってもいいです。

qiita-Django-supermarket/
django-admin startproject api

実行後、ディレクトリ構成は以下になります。

qiita-Django-supermarket
|-- requirements.txt
|-- README.md
|-- LICENSE
|-- CHANGELOG.md
|-- api
|-- |--api
|-- |-- |-- __init__.py
|-- |-- |-- settings.py
|-- |-- |-- urls.py
|-- |-- |-- wsgi.py
|-- |--manage.py

settings.pyファイルをエディタで開き、以下の通り内容を変更します。
ここでsetting.pyを環境分けしてもいいですが、今回は省きます。
興味のある方は下記の記事を参考にしてください。
Django実践開発入門

api/settings
INSTALLED_APPS = [
    ...
    'rest_framework',
]

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

USE_I18N = True

USE_L10N = True

USE_TZ = True

サーバー立ち上げて、現時点問題ないか確認します。

python manage.py runserver

下記の画面確認できたらオーケーです:relaxed:
キャプチャ.PNG

新規 App

モジュールの構成図に従って、Appを作ります。

qiita-Django-supermarket/api
python manage.py startapp users
python manage.py startapp goods
python manage.py startapp trade
python manage.py startapp user_operation
mkdir apps

その後、新規作った四つのファイルをappsに入れます。
完了後、ディレクトリは下記のようになります。

```shell
qiita-Django-supermarket
|-- requirements.txt
|-- README.md
|-- LICENSE
|-- CHANGELOG.md
|-- api
|-- |--apps
|-- |-- |--goods
|-- |-- |--trade
|-- |-- |--user_operation
|-- |-- |--users 
|-- |--api
|-- |-- |-- __init__.py
|-- |-- |-- settings.py
|-- |-- |-- urls.py
|-- |-- |-- wsgi.py
|-- |--manage.py

そしてappsを souruces Rootに設定します。
PyCharmの操作方法は
1.appsを右クリック
2. Mark Directory as
3. souruces Root
完了後 ファイルが青くなってることが確認できますキャプチャ.PNG

その後、settings.pyファイルをエディタで開き、以下の通り内容を変更します。

settings.py
...
import sys
sys.path.insert(0, BASE_DIR)
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
...
INSTALLED_APPS = [
     ...
    'users',
    'goods',
    'trade',
    'user_operation',
     ...
]

model

モデルの作り方もモジュール構成図に従い作ります。

users

先ずはusersのモデルから変更します。
ここでUserProfile クラスにAbstractUserを継承させて、Django本来のuserモデルを上書きします。

users/models.py

from datetime import datetime

from django.db import models
from django.contrib.auth.models import AbstractUser


class UserProfile(AbstractUser):
    """
    ユーザー
    """
    name = models.CharField(max_length=30, null=True, blank=True, verbose_name="名前")
    birthday = models.DateField(null=True, blank=True, verbose_name="生年月日")
    mobile = models.CharField(max_length=20, verbose_name="携帯番号")
    gender = models.CharField(max_length=6, choices=(("male", "男性"), ("female", "女性"), ("secret", "非公開")),
                              default="male", verbose_name="性別")
    email = models.CharField(max_length=100, null=True, blank=True, verbose_name="アドレス")

    class Meta:
        verbose_name = "ユーザー"
        verbose_name_plural = verbose_name

    def __str__(self):
       return self.username


class VerifyCode(models.Model):
    """
    メール認証コード
    """
    code = models.CharField(max_length=10, verbose_name="メール認証コード")
    mobile = models.CharField(max_length=11, verbose_name="携帯番号")
    add_time = models.DateTimeField(default=datetime.now, verbose_name="作成時間")

    class Meta:
        verbose_name = "メール認証コード"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.code

上書きのためにsettings.pyに一個変更を追加。
VerifyCodeはユーザー登録時に使用する認証codeの検証のために作ってますが、使わない可能性もあります。

settings.py
...
AUTH_USER_MODEL = "users.UserProfile"
...

管理画面を作成するため、admin.pyも変更します。

users/admin.py
from django.contrib import admin

from .models import VerifyCode, UserProfile


class UserProfileAdmin(admin.ModelAdmin):
    list_display = ['name', 'birthday', 'mobile', 'gender', 'email']


class VerifyCodeAdmin(admin.ModelAdmin):
    list_display = ['code', 'mobile', "add_time"]


admin.site.register(VerifyCode, VerifyCodeAdmin)
admin.site.register(UserProfile, UserProfileAdmin)

goods

次はgoods Appmodels.pyを書きます

goods/models.py
from datetime import datetime

from django.db import models


class GoodsCategory(models.Model):
    """
    商品カテゴリー
    """
    CATEGORY_TYPE = (
        (1, "一級カテゴリー"),
        (2, "二級カテゴリー"),
        (3, "三級カテゴリー")
    )

    name = models.CharField(default="", max_length=50, verbose_name="カテゴリー名", help_text="カテゴリー名")
    code = models.CharField(default="", max_length=30, verbose_name="カテゴリーコード", help_text="カテゴリーコード")
    desc = models.TextField(default="", verbose_name="カテゴリー説明", help_text="カテゴリー説明")
    category_type = models.IntegerField(choices=CATEGORY_TYPE, verbose_name="カテゴリーレベル", help_text="カテゴリーレベル")
    parent_category = models.ForeignKey("self", null=True, blank=True, verbose_name="親カテゴリー", help_text="親カテゴリー",
                                        on_delete=models.CASCADE, related_name="sub_cat")
    is_tab = models.BooleanField(default=False, verbose_name="ナビなのか", help_text="ナビなのか")
    add_time = models.DateTimeField(default=datetime.now, verbose_name="挿入時間")

    class Meta:
        verbose_name = "商品カテゴリー"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name


class GoodsCategoryBrand(models.Model):
    """
    ブランド名
    """
    category = models.ForeignKey(GoodsCategory, related_name="brands", null=True, blank=True,
                                 verbose_name="商品カテゴリー名", on_delete=models.CASCADE)
    name = models.CharField(default="", max_length=30, verbose_name="ブランド名", help_text="ブランド名")
    desc = models.CharField(default="", max_length=200, verbose_name="ブランド説明", help_text="ブランド説明")
    image = models.ImageField(max_length=200, upload_to="brands/")
    add_time = models.DateTimeField(default=datetime.now, verbose_name="挿入時間")

    class Meta:
        verbose_name = "ブランド"
        verbose_name_plural = verbose_name
        db_table = "goods_goodsbrand"

    def __str__(self):
        return self.name


class Goods(models.Model):
    """
    商品
    """
    category = models.ForeignKey(GoodsCategory, null=True, blank=True,
                                 verbose_name="商品カテゴリー", on_delete=models.CASCADE)
    goods_sn = models.CharField(max_length=50, default="", verbose_name="商品識別番号")
    name = models.CharField(max_length=100, verbose_name="商品名")
    click_num = models.IntegerField(default=0, verbose_name="クリック数")
    sold_num = models.IntegerField(default=0, verbose_name="販売数")
    fav_num = models.IntegerField(default=0, verbose_name="お気に入り登録数")
    goods_num = models.IntegerField(default=0, verbose_name="在庫数")
    market_price = models.FloatField(default=0, verbose_name="原価")
    shop_price = models.FloatField(default=0, verbose_name="販売値段")
    goods_brief = models.TextField(max_length=500, verbose_name="商品説明")
    ship_free = models.BooleanField(default=True, verbose_name="送料負担")
    goods_front_image = models.ImageField(max_length=200, upload_to="goods/images/",
                                          null=True, blank=True, verbose_name="表紙")
    is_new = models.BooleanField(default=False, verbose_name="新品なのか")
    is_hot = models.BooleanField(default=False, verbose_name="売れているのか")
    add_time = models.DateTimeField(default=datetime.now, verbose_name="挿入時間")

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

    def __str__(self):
        return self.name


class GoodsImage(models.Model):
    """
    商品swiperImages
    """
    goods = models.ForeignKey(Goods, verbose_name="商品", related_name="images", on_delete=models.CASCADE)
    image = models.ImageField(upload_to="", verbose_name="画像", null=True, blank=True)
    add_time = models.DateTimeField(default=datetime.now, verbose_name="挿入時間")

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

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


class Banner(models.Model):
    """
    swiper用の商品image
    """
    goods = models.ForeignKey(Goods, verbose_name="商品", on_delete=models.CASCADE)
    image = models.ImageField(upload_to="banner", verbose_name="ホームページswiper用画像")
    index = models.IntegerField(default=0, verbose_name="swiper順番")
    add_time = models.DateTimeField(default=datetime.now, verbose_name="挿入時間")

    class Meta:
        verbose_name = "swiper用の商品image"
        verbose_name_plural = verbose_name

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


class IndexAd(models.Model):
    category = models.ForeignKey(GoodsCategory, related_name="category",
                                 verbose_name="商品カテゴリー", on_delete=models.CASCADE)
    goods = models.ForeignKey(Goods, related_name='goods', on_delete=models.CASCADE)

    class Meta:
        verbose_name = "ホームページ商品カテゴリー広告"
        verbose_name_plural = verbose_name

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


class HotSearchWords(models.Model):
    """
    人気キーワード
    """
    keywords = models.CharField(default="", max_length=20, verbose_name="人気キーワード")
    index = models.IntegerField(default=0, verbose_name="並び順")
    add_time = models.DateTimeField(default=datetime.now, verbose_name="挿入時間")

    class Meta:
        verbose_name = "人気キーワード"
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.keywords

管理画面を作成するため、admin.pyも変更します。

goods/admin.py
from django.contrib import admin
from django.contrib.admin.options import InlineModelAdmin


from .models import Goods, GoodsImage, GoodsCategory, GoodsCategoryBrand, Banner, HotSearchWords, IndexAd


class GoodsAdmin(admin.ModelAdmin):
    list_display = ["id", "name", "click_num", "sold_num", "fav_num", "goods_num", "market_price",
                    "shop_price", "goods_brief", "is_new", "is_hot", "add_time"]
    search_fields = ['name', ]
    list_editable = ["is_hot", ]
    list_filter = ["name", "click_num", "sold_num", "fav_num", "goods_num", "market_price",
                   "shop_price", "is_new", "is_hot", "add_time", "category__name"]

    class GoodsImagesInline(InlineModelAdmin):
        model = GoodsImage
        exclude = ["add_time"]
        extra = 1
        style = 'tab'

    inlines = [GoodsImagesInline]


class GoodsCategoryAdmin(admin.ModelAdmin):
    list_display = ["name", "category_type", "parent_category", "add_time"]
    list_filter = ["category_type", "parent_category", "name"]
    search_fields = ['name', ]


class GoodsBrandAdmin(admin.ModelAdmin):
    list_display = ["category", "image", "name", "desc"]


class BannerGoodsAdmin(admin.ModelAdmin):
    list_display = ["goods", "image", "index"]


class HotSearchAdmin(admin.ModelAdmin):
    list_display = ["keywords", "index", "add_time"]


class IndexAdAdmin(admin.ModelAdmin):
    list_display = ["category", "goods"]


admin.site.register(Goods, GoodsAdmin)
admin.site.register(GoodsCategory, GoodsCategoryAdmin)
admin.site.register(Banner, BannerGoodsAdmin)
admin.site.register(GoodsCategoryBrand, GoodsBrandAdmin)
admin.site.register(HotSearchWords, HotSearchAdmin)
admin.site.register(IndexAd, IndexAdAdmin)

tradeuser_operationはしばらく使わないので一旦飛ばします。

settings.pyurls.pyに以下の内容を追加します。

settings.py
...
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
...
api/urls.py
from django.contrib import admin
from django.urls import path, re_path
from django.views.static import serve
from api.settings import MEDIA_ROOT

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^media/(?P<path>.*)$', serve, {"document_root": MEDIA_ROOT}),
]

下記のcodeを実行しましょう。

api

python manage.py makemigrations 
python manage.py migrate
python manage.py createsuperuser

完了後


 python manage.py runserver 

でサーバーを立ち上げ、ローカルの管理画面にアクセスしてみてください。
http://127.0.0.1:8000/admin
ログインして、下記の内容確認できたらオーケーです。

キャプチャ.PNG

次回予告

Xadminというパッケージ使用して、管理画面を変更します。
実際Django REST framework の機能を使用して、APIを作っていきます。

最後まで読んで頂き、ありがとうございました。:relaxed:

次回

SEIYU風のECサイトを作りましょう(2)Xadminを使って管理画面を一新します

31
41
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
41