55
53

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Django Oscar (ECパッケージ) 日本利用ガイド

Last updated at Posted at 2015-12-07

Python で(多分)最も使われている全部入り(フルスタック) Webフレームワーク、Django。そのDjango 上で動く、(多分)最も勢いがある ECパッケージ、Oscar。

その Oscar を日本で使うためのガイドとして書きました。

Django-Oscar ドキュメント: https://django-oscar.readthedocs.org/

リポジトリ: https://github.com/django-oscar/django-oscar

Oscar の特徴

  • Python + Django 環境上で動くので、経験者であればカスタマイズが容易
  • Python3 でも動く
  • Django は最新の 1.8 必須。(追記:1.9リリースされてた)
  • 修正BSDライセンス(BSD 3-Clause License (Revised))。Djangoと同じ。
  • Bootstrap 入り。デモテンプレートからレスポンシブになってます。
  • 多言語対応済み。日本語のPOファイルも最初から入ってますが、それほど充実してはいないです。
  • 多通貨対応済み。通貨の型は標準では小数点付き10進数なので、日本人の僕は最初少し戸惑った。
  • 最初からさまざまな国の住所が登録できる。
  • 決済システムは最初は入ってないので、pip で入れるか自分で書く。
  • コアコード以外を柔軟にカスタマイズ可能。例えば、モデルにフィールドを追加したり、ビューの動作を変えたりなどは元コードに手を入れなくてもできる。(コアコードを修正するならモンキーパッチかリポジトリのフォークかな…)
  • 商品(カタログ) は、クラス > プロダクト > 在庫&料金 という3段構成。
  • 在庫&料金クラスは、通貨別にそれぞれ在庫が設定できる。
  • カテゴリクラスがある。 カテゴリ:プロダクトは 多:多
  • 商品の画像は、通常サイトから画像ファイルをアップロードする形式で、外部サービスに画像を登録して URL だけモデルに書く、という使い方は最初はできない。モデルのカスタマイズとテンプレート修正が必要。
  • 税率は、税率クラスをハードコーディングで書く。
  • スタイルシートは less
  • 管理サイトにログインする管理者アカウントと、一般顧客のアカウントは同じモデル(テーブル)に作ることになる。パーミッションフラグで権限を分ける。このモデルの分離をしたいならかなり困難そう。

最初から入ってる機能

  • レビュー機能
  • ウィッシュリスト
  • オファー (特定条件指定のセール機能)
  • バウチャー(いわゆるクーポンコード)
  • 管理サイト(ダッシュボード)
  • サムネイル生成
  • JS, CSS圧縮 (django-compressor)

など。

レビューなどの機能を OFF にするのは比較的簡単にできます。(settingsで可能)

そういえば、日本でよくある「ポイント」機能は入ってません。

モデルのフォーク

Oscar 標準機能を延長(継承)したい場合、fork という機能を使います。

まず、プロジェクト直下に oscar_fork ( とか myapps とか ) といったディレクトリを作り、

$ ./manage.py oscar_fork_app catalogue oscar_fork

を実行すると、oscar_fork/catalogue が出来ます。

このフォークモジュールでは、

  • モデルの拡張
  • ビューの拡張、無効化
  • URL の追加、無効化

などが行えます。

モデルフィールドの追加例

例えば、catalogue.Product に、thumbnail_url フィールドを追加したいなら

oscar_fork/catalogue/models.py
from django.db import models
from oscar.apps.catalogue.abstract_models import AbstractProduct

class Product(AbstractProduct):
    image_url = models.URLField(default='', blank=True)
    thumbnail_url = models.URLField(default='', blank=True)

from oscar.apps.catalogue.models import *  # noqa

として、INSTALLED_APPS に

settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    ....
]
INSTALLED_APPS += get_core_apps([
    'oscar_fork.catalogue',
])

と書くことで、INSTALLED_APPS を延長します。

./manage.py makemigrations でマイグレーションファイルが出来るので、./manage.py migrate でスキーママイグレーションします。

ビューの拡張

カート (checkout) のビューを拡張したいなら

$ ./manage.py oscar_fork_app checkout oscar_fork

してから

oscar_fork/checkout.views.py
from oscar.apps.checkout import views

class IndexView(views.IndexView):
    def get(self, request, *args, **kwargs):
        # get処理...

のように書けます。

INSTALLED_APPS にも追加

settings.py
INSTALLED_APPS += get_core_apps([
    'oscar_fork.catalogue',
    'oscar_fork.checkout',
])

URLs の追加、編集

URLs を追加したり無効化したい場合は、app.py を書きます。

$ ./manage.py oscar_fork_app customer oscar_fork
oscar_fork/customer/app.py
from oscar.apps.customer import app

class CustomerApplication(app.CustomerApplication):
    def get_urls(self):
        urls = [
            # Login, logout and register doesn't require login
            url(r'^login/$', self.login_view.as_view(), name='login'),
            url(r'^logout/$', self.logout_view.as_view(), name='logout'),
            # url(r'^register/$', self.register_view.as_view(),
            # name='register'),

register のURL をコメントアウトすることで、無効化したりできます。

INSTALLED_APPS にも追加。(詳細略)

日本国内向けに使うにはどうしたら良いか

まずは、Djangoの settings を日本向けに。

settings.py
LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

Oscar 特有設定を日本向けに

settings.py
OSCAR_DEFAULT_CURRENCY = 'JPY'

OSCAR_CURRENCY_FORMAT = '¤#,##0'

登録国を日本のみに

$ ./manage.py oscar_populate_countries

で、国モデル ( address_country ) に249カ国の初期データが入ります。

これの、is_shipping_country を Japan のみ 1に、他は 0 にすると、住所登録時に国名選択が出てこなくなります (日本がデフォルトになる)。

Django シェルからでも、SQL でも。

mysql> UPDATE address_country SET is_shipping_country = 0 WHERE printable_name != 'Japan';

POファイルをひたすら書いて翻訳

How do I translate Oscar? — django-oscar 0.5 documentation
https://django-oscar.readthedocs.org/en/releases-0.5/howto/how_do_i_translate_oscar.html

ここを参考に

$ mkdir locale i18n
$ ln -s $PATH_TO_OSCAR i18n/oscar
$ ./manage.py makemessages --symlinks --locale=ja

(うまくいかない場合、多分 gettext がインストールされてないとか。僕の場合(mac)は brew install gettext --force が必要でした)

locale/ja/LC_MESSAGES/django.po を編集

msgid "email address"
msgstr "メールアドレス"

msgid "First name"
msgstr "姓"

msgid "Last name"
msgstr "名"

msgid "View basket"
msgstr "カートを見る"

msgid "Basket total"
msgstr "合計金額"

msgid "Basket total:"
msgstr "合計金額:"

msgid "In stock"
msgstr "在庫有り"

msgid "Shipping"
msgstr "送料"

msgid "Total"
msgstr "小計"

msgid "Order total"
msgstr "合計金額"

msgid "State/County"
msgstr "都道府県"

msgid "City"
msgstr "市区町村"

msgid "First line of address"
msgstr "町名/字/番地"

msgid "Second line of address"
msgstr "建物/部屋番号"

...

(※ First name = 姓, Last name = 名 は意図して書いており、間違ったわけではないです。こっちのほうが僕は使いやすいので。)

poファイルを書いたら、

$ ./manage.py compilemessages

を実行し mo ファイルを作っておく。これでサイトが日本語表示になります。

税率の変更

デフォルトは、固定税率 0% になってます。日本は消費税 8% なので変更が必要です。

税率は、partner/strategy.py にあるので

$ ./manage.py oscar_fork_app partner oscar_fork

してから、

oscar_fork/partner/strategy.py
from decimal import Decimal as D
from oscar.apps.partner import strategy

class Selector(object):
    def strategy(self, request=None, user=None, **kwargs):
        return JPStrategy()

class JP(strategy.FixedRateTax):
    rate = D('0.08')
    exponent = D('1.')

    def pricing_policy(self, product, stockrecord):
        if not stockrecord:
            return prices.Unavailable()
        rate = self.get_rate(product, stockrecord)
        exponent = self.get_exponent(stockrecord)
        tax = (stockrecord.price_excl_tax * rate).quantize(
            exponent, rounding=ROUND_DOWN)
        return prices.TaxInclusiveFixedPrice(
            currency=stockrecord.price_currency,
            excl_tax=stockrecord.price_excl_tax,
            tax=tax)

    def parent_pricing_policy(self, product, children_stock):
        stockrecords = [x[1] for x in children_stock if x[1] is not None]
        if not stockrecords:
            return prices.Unavailable()

        # We take price from first record
        stockrecord = stockrecords[0]
        rate = self.get_rate(product, stockrecord)
        exponent = self.get_exponent(stockrecord)
        tax = (stockrecord.price_excl_tax * rate).quantize(
            exponent, rounding=ROUND_DOWN)

        return prices.FixedPrice(
            currency=stockrecord.price_currency,
            excl_tax=stockrecord.price_excl_tax,
            tax=tax)

class JPStrategy(strategy.UseFirstStockRecord, strategy.StockRequired,
                 JP, strategy.Structured):
    pass

このように書けます。

※ JPクラスが長ったらしくて少し冗長に見えると思います。
以前は、この JP クラスを

class JP(strategy.FixedRateTax):
    rate = D('0.08')

こう書いていました。この場合、金額の小数点以下がそのまま扱われます。
例えば、税抜70円の商品の場合、税込75.6円として内部で計算されます。小数点以下を考慮します。

これだと、私のサービスだと不都合がありましたので、以下のように変更した所

class JP(strategy.FixedRateTax):
    rate = D('0.08')
    exponent = D('1.')

小数点以下は四捨五入となります。税抜70円商品の場合、税込76円になります。

今回は、小数点以下は切り捨てとしたかったので、pricing_policy, parent_pricing_policy 両方のメソッドをオーバーライドし、Decimalquantize メソッドの引数に rounding=ROUND_DOWN を入れるようにしました。

ただ 端数切捨てにしたかっただけでメソッドをまるごとオーバーライドするのはちょっとエレガントではないですね。なんか良い方法ないでしょうか。

decimal.setcontext で実現できるんでしょうかね。他に影響ありそうで怖くてできません。

例によって INSTALLED_APPS に追加

settings.py
INSTALLED_APPS += get_core_apps([
    ...
    'oscar_fork.partner',
])

ロジックで分岐など書けるので、複雑な処理も書けます。関税など。

これで、ほぼ日本で使えるようになったと思います。

あとはひたすらテンプレート作成と翻訳ですね。

トップページはどこ?

ちなみに。トップページは promotions の中にあります。
ビューは oscar.apps.promotions.views.HomeView
テンプレートは promotions/home.html です。探すと少し時間がかかるので、書き記しておきます。

その他必要な機能

決済モジュール

paypal は、 django-oscar-paypal モジュールがあって比較的簡単に使えるらしい。エクスプレスチェックアウト と payflow 対応。多分、エクスプレスチェックアウトはリンクが出るだけ。

僕は webpay でやろうと思ったので自分で書きました。

payment.views.PaymentDetailsViewhandle_payment をオーバーライドして書くことになります。

55
53
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
55
53

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?