前回の記事
Django REST framework + Vue.js「SEIYU風のECサイトを作りましょう」全6回(予定)
前書き
何かを開発する場合、依頼者からの要求を満足させる必要があります。
その依頼者とは、お客様や上司、マネージャーなどが該当します。
では、今回のECサイトにはどのような要求があるのでしょうか。
機能要求分析
SEIYUのECサイトを分析して機能を割り出すと、大体以下のものになります。
- 商品展示
- 商品画像
- 特定の商品展示
- 商品検索
- 商品詳細
- ログイン
- 一般ユーザー関連
- 商品レビュー
- 商品をかごに追加できる
- 商品をお気に入りにする
- 出品者と連絡
- 支払い
まだまだあるかもしれませんが、以上の機能さえ満たせば、ECサイトとして機能します。
機能に基づいて、モジュールの構成を図にしてみましょう。
ここまで来ればどんなモジュールを開発する必要があるのかは、明確になりました。
プロジェクト初期化
仮想環境のセットアップ
参考資料
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日では、以下のバージョンのライブラリがインストールされました。
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で作ってもいいです。
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実践開発入門
INSTALLED_APPS = [
...
'rest_framework',
]
LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'
USE_I18N = True
USE_L10N = True
USE_TZ = True
サーバー立ち上げて、現時点問題ないか確認します。
python manage.py runserver
新規 App
モジュールの構成図に従って、Appを作ります。
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
完了後 ファイルが青くなってることが確認できます
その後、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モデルを上書きします。
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の検証のために作ってますが、使わない可能性もあります。
...
AUTH_USER_MODEL = "users.UserProfile"
...
管理画面を作成するため、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 Appの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も変更します。
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)
tradeとuser_operationはしばらく使わないので一旦飛ばします。
settings.pyとurls.pyに以下の内容を追加します。
...
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
...
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を実行しましょう。
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
完了後
python manage.py runserver
でサーバーを立ち上げ、ローカルの管理画面にアクセスしてみてください。
http://127.0.0.1:8000/admin
ログインして、下記の内容確認できたらオーケーです。
次回予告
Xadminというパッケージ使用して、管理画面を変更します。
実際Django REST framework の機能を使用して、APIを作っていきます。
最後まで読んで頂き、ありがとうございました。
###次回
SEIYU風のECサイトを作りましょう(2)Xadminを使って管理画面を一新します