django oscar 簡易チュートリアル

ecサイトを一人でパパッと作る必要性が生じたので、パッケージやフレームワークをフル活用すればパパッとECサイトが完成する（はず）。てことで、ウェブフレームワークにDjango、ECパッケージにOscarを使用を決定。決めたのはいいですが日本語資料が少ない。英語版の公式ドキュメントも「詳しくは[Sandbox](https://latest.oscarcommerce.com/)と[github](https://github.com/django-oscar/django-oscar/blob/master/sandbox)で」と優しくないうえに分かりにくい。

この記事は一通りECサイトを構築~~した気分に~~するまでの手順を示します。基本的にdjango-oscarの[django-oscar/Building your own shop](https://django-oscar.readthedocs.io/en/stable/internals/getting_started.html)をなぞっているだけですが参考に。

最終的に決済機能のないECサイトを構築します。また、これはあくまでもチュートリアルです。

## 開発環境
- Mac OS X
- Python==3.8.0
- Django==2.2.9
- django-oscar==2.0.4

## Setup

[ドキュメント](https://django-oscar.readthedocs.io/en/stable/internals/getting_started.html)では、`mkvirtualenv`か`virtualenv`を使ってますが、今回は自分のパソコンに元から入っていたAnacondaを活用します。

Anacondaで仮想環境を構築する。まず、python 3.8環境を構築。

```bash
$ conda create -n py38 python=3.8
```

仮想環境を有効化。

```bash
$ conda info -e #環境確認
$ conda activate py38
```

`django-oscar`をpipでインストール。

```
$ pip install django-oscar
```

一部足りないパッケージがあるので、それもインストール。

```bash
$ pip install sorl-thumbnail
$ pip install pysolr
```

なお、2020年1月時点だと、バージョンは以下の通り

- pysolr==3.8.1
- sorl-thumbnail==12.5.0

Djangoプロジェクトを作成する

```
$ django-admin startproject frobshop
```


## Django設定(settings.py)

`frobshop.frobshop.settings.py`の行頭に以下を追記。Oscarのデフォルト設定がインポートされる。

```python:frobshop.frobshop.settings.py
from oscar.defaults import *
```

Oscarのcontext processorsをテンプレートに追加。

```python:frobshop.frobshop.settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        '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',
                'oscar.apps.search.context_processors.search_form',
                'oscar.apps.checkout.context_processors.checkout',
                'oscar.apps.customer.notifications.context_processors.notifications',
                'oscar.core.context_processors.metadata',
            ],
        },
    },
]
```

`INSTALLED_APPS`を以下のように書き換え、`SITE_ID`も設定。

```python:frobshop.frobshop.settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'django.contrib.sites',
    'django.contrib.flatpages',

    'oscar',
    'oscar.apps.analytics',
    'oscar.apps.checkout',
    'oscar.apps.address',
    'oscar.apps.shipping',
    'oscar.apps.catalogue',
    'oscar.apps.catalogue.reviews',
    'oscar.apps.partner',
    'oscar.apps.basket',
    'oscar.apps.payment',
    'oscar.apps.offer',
    'oscar.apps.order',
    'oscar.apps.customer',
    'oscar.apps.search',
    'oscar.apps.voucher',
    'oscar.apps.wishlists',
    'oscar.apps.dashboard',
    'oscar.apps.dashboard.reports',
    'oscar.apps.dashboard.users',
    'oscar.apps.dashboard.orders',
    'oscar.apps.dashboard.catalogue',
    'oscar.apps.dashboard.offers',
    'oscar.apps.dashboard.partners',
    'oscar.apps.dashboard.pages',
    'oscar.apps.dashboard.ranges',
    'oscar.apps.dashboard.reviews',
    'oscar.apps.dashboard.vouchers',
    'oscar.apps.dashboard.communications',
    'oscar.apps.dashboard.shipping',

    # 3rd-party apps that oscar depends on
    'widget_tweaks',
    'haystack',
    'treebeard',
    'sorl.thumbnail',
    'django_tables2',
]

SITE_ID = 1
```

`oscar.apps.basket.middleware.BasketMiddleware`と`django.contrib.flatpages.middleware.FlatpageFallbackMiddleware`を`MIDDLEWARE`の設定に追加。

```python:frobshop.frobshop.settings.py
MIDDLEWARE = (
    ...
    'oscar.apps.basket.middleware.BasketMiddleware',
    'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
)
```

メールアドレスで認証できるように`AUTHENTICATION_BACKENDS`の設定を追加。

```python:frobshop.frobshop.settings.py
AUTHENTICATION_BACKENDS = (
    'oscar.apps.customer.auth_backends.EmailBackend',
    'django.contrib.auth.backends.ModelBackend',
)
```

### 日本対応

日本向け対応するには以下のように、言語コードと時間帯を書き換える。

```python:frobshop.frobshop.settings.py
LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'
```

通貨設定と通貨のフォーマットを設定（oscarのデフォルトのフォーマットだと小数点第二位まで表示してしまうため。）

```python:frobshop.frobshop.settings.py
OSCAR_DEFAULT_CURRENCY = 'JPY'
OSCAR_CURRENCY_FORMAT = '¤#,##0'
```

## URLs

`frobshop.frobshop.urls.py`を以下のように書き換える。

```python:frobshop.frobshop.urls.py
from django.apps import apps
from django.urls import include, path  # > Django-2.0
from django.contrib import admin

urlpatterns = [
    path('i18n/', include('django.conf.urls.i18n')),  # > Django-2.0

    # The Django admin is not officially supported; expect breakage.
    # Nonetheless, it's often useful for debugging.

    path('admin/', admin.site.urls),  # > Django-2.0

    path('', include(apps.get_app_config('oscar').urls[0])),  # > Django-2.0
]
```

## Search backend

検索システムを設定する。とりあえず試したい場合は以下の設定をする。

```python:frobshop.frobshop.settings.py
HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'haystack.backends.simple_backend.SimpleEngine',
    },
}
```

`Apache Solr`を設定する場合は事前に[Apache Solrをセットアップ](https://django-oscar.readthedocs.io/en/stable/howto/how_to_setup_solr.html)した上で、以下を`frobshop.frobshop.settings.py`に追記。

```python:frobshop.frobshop.settings.py
HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'haystack.backends.solr_backend.SolrEngine',
        'URL': 'http://127.0.0.1:8983/solr',
        'INCLUDE_SPELLING': True,
    },
}
```

## データベース

すぐにでも動作確認したい場合はデータベースに`sqlite`を使う。`frobshop.frobshop.settings.py`を編集して`DATABASES`の設定を以下のように書き換える。

```python:frobshop.frobshop.settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
        'ATOMIC_REQUESTS': True,
    }
}
```

## サイトの起動確認

マイグレーションを実施。

```bash
$ python manage.py migrate
```

サーバーを起動してサイトが起動できるかどうかを確認。

```bash
$ python manage.py runserver
```

## 初期データ

ここまでだと、商品も登録ユーザーも存在しないECサイトが構築される。諸々の設定を行なって、ECサイトっぽくしていきます。

### 国モデル

国モデル ( address_country ) に249カ国の初期データを入れる。

```bash
$ pip install pycountry
$ python manage.py oscar_populate_countries
```

249カ国は多すぎるので、登録国を日本のみにする。まず、DBへの接続してから、is_shipping_country を Japan のみ 1に、他は 0 にする。住所登録時に国名選択が出てこなくなる(日本がデフォルトになる)

```bash
$ python manage.py dbshell
SQLite version 3.30.1 2019-10-10 20:19:45
Enter ".help" for usage hints.
sqlite> UPDATE address_country SET is_shipping_country = 0 WHERE printable_name != 'Japan';
```

### ユーザーデータ

[モデルに対する初期データを投入する](https://docs.djangoproject.com/ja/2.2/howto/initial-data/)を参考にデータを投入する。

まずはユーザーデータを投入する。[Sandboxに登録されているユーザー情報](https://github.com/django-oscar/django-oscar/blob/master/sandbox/fixtures/auth.json)を投入。

```bash
$ python manage.py loaddata frobshop/fixtures/auth.json
Installed 2 object(s) from 1 fixture(s)
```

管理者ユーザーは以下の設定になっています。

```
username: superuser
email: superuser@example.com
password: testing
```

スタッフアカウントは以下の通り。

```
username: staff
email: staff@example.com
password: testing
```

### 商品データ

商品データを投入する。[公式のSandbox site](https://github.com/django-oscar/django-oscar/blob/master/sandbox)には二種類商品データが存在する。[multi-stockrecord-product.json](https://github.com/django-oscar/django-oscar/blob/master/sandbox/fixtures/multi-stockrecord-product.json)と[child_products.json](https://github.com/django-oscar/django-oscar/blob/master/sandbox/fixtures/child_products.json)。

それぞれの違いを説明する。[child_products.json](https://github.com/django-oscar/django-oscar/blob/master/sandbox/fixtures/child_products.json)は一つの商品でも種類が違う場合（例えば、Tシャツだとサイズとか色とか）に対応したい場合の設定の仕方を示している。例えば、Tシャツの場合は、`Attributes`をサイズ`L`,`M`,`S`と設定する。

[multi-stockrecord-product.json](https://github.com/django-oscar/django-oscar/blob/master/sandbox/fixtures/multi-stockrecord-product.json)はecサイトを運営している場合、在庫を管理している倉庫あるいは発注先が複数存在する場合がある。複数の発注先をoscarでは`partner.stockrecord`で管理している。

商品データをとりあえず投入したい場合は、いずれかのjsonをデータベースに投入する。

```bash
$ python manage.py loaddata frobshop/fixtures/multi-stockrecord-product.json
```

oscarがどのように商品管理するのか、考え方を示したドキュメントを以下にリンクを貼っておきます。

- [modelling_your_catalogue](https://django-oscar.readthedocs.io/en/stable/topics/modelling_your_catalogue.html)
- [glossary](https://django-oscar.readthedocs.io/en/stable/ref/glossary.html)

### オーダーパイプライン
ecサイト経由で注文が来た場合、どのように注文を処理するかをオーダーパイプライン(order pipeline)で設定する。例えば、`settings.py`に以下を追記する。以下の例だと`Pending`が最初のステータスで、`Pending`→`Being processed`→`Processed`の順に進んでいく。途中でキャンセルされると`Cancelled`のステータスになることを想定している。

```python:frobshop.frobshop.settings.py
OSCAR_INITIAL_ORDER_STATUS = 'Pending'
OSCAR_INITIAL_LINE_STATUS = 'Pending'
OSCAR_ORDER_STATUS_PIPELINE = {
    'Pending': ('Being processed', 'Cancelled',),
    'Being processed': ('Processed', 'Cancelled',),
    'Cancelled': (),
}
```

## メール設定

上記の設定のままだと注文確定時（`Preview order`の画面の`Place order`を押す時）に`ConnectionRefusedError at /checkout/preview/`が出る。

[Building a full ecommerce site part 5: testing directing customers to Oscar's thank-you page after payment](https://notathoughtexperiment.me/blog/building-a-full-ecommerce-site-part-5-send-clients-to-the-thank-you-page-after-payment/)に理由が書いているけれど、読んでもよく分からなかった。

対処法は`settings.py`に以下のように`EMAIL_BACKEND`を追記する。

```python:frobshop.frobshop.settings.py
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
```

## まとめ
とりあえず、ここまで設定すればECサイトの機能が一通り網羅されます(決済機能以外)。

さらにECサイトを構築する (例えば商品に画像を追加するなど)には[Github上のSandboxサイトの設定](https://github.com/django-oscar/django-oscar/tree/master/sandbox)を参考に構築してみてください。

気が向いたら決済機能の実装を書きたいと思います。

## 参考サイト
以下のサイトを参考にしました。

- [django-oscar/Building your own shop](https://django-oscar.readthedocs.io/en/stable/internals/getting_started.html)
- [Django Oscar (ECパッケージ) 日本利用ガイド](https://qiita.com/ytyng/items/a4ae77df8bc4c5506d19)
- [コピペで始めるDjango Oscor](https://qiita.com/shinriyo/items/a59412f4569171e41606)
- [ゼロからはじめる Django で ECサイト構築（その２：Django Oscar の Sandbox サイト構築）](https://akiyoko.hatenablog.jp/entry/2016/05/31/014711#sandbox-2)
- [Building a full ecommerce site part 5: testing directing customers to Oscar's thank-you page after payment](https://notathoughtexperiment.me/blog/building-a-full-ecommerce-site-part-5-send-clients-to-the-thank-you-page-after-payment/)