この前 4.2.2 にアップデートした Mezzanine サイトに、Cartridge 0.12 を導入してみました。
Mezzanine は、Python製の WordPress風フルスタックCMSフレームワークですが、一方の Cartridge は、Mezzanine 専用に作られた、Mezzanine に ECサイト機能を搭載するためのアプリケーションです。 *1
イメージとしてはこんな感じです。
具体的には、商品登録やセール設定、ディスカウント設定、注文管理などの機能を備えたバックオフィス(Django Admin を拡張)、ショッピングカート、PayPal および Stripe に対応した決済モジュール(プラガブルに変更可能)などの機能が付きます。
(ショッピングカート機能)
(バックオフィス機能)
1. 導入手順
Mezzanine に Cartridge 0.12 を導入する手順は以下の通りです。
Cartridge 0.12.0 をインストールします。
(akiyokoproject)$ pip install cartridge==0.12.0 Successfully installed cartridge-0.12.0 pyPdf2-1.26.0 reportlab-3.3.0 xhtml2pdf-0.0.6
config/settings.py に Cartridge 用の設定を追記します。
--- a/config/settings.py +++ b/config/settings.py @@ -7,6 +7,76 @@ from django.utils.translation import ugettext_lazy as _ ###################### +# CARTRIDGE SETTINGS # +###################### + +# The following settings are already defined in cartridge.shop.defaults +# with default values, but are common enough to be put here, commented +# out, for conveniently overriding. Please consult the settings +# documentation for a full list of settings Cartridge implements: +# http://cartridge.jupo.org/configuration.html#default-settings + +# Sequence of available credit card types for payment. +# SHOP_CARD_TYPES = ("Mastercard", "Visa", "Diners", "Amex") + +# Setting to turn on featured images for shop categories. Defaults to False. +# SHOP_CATEGORY_USE_FEATURED_IMAGE = True + +# Set an alternative OrderForm class for the checkout process. +# SHOP_CHECKOUT_FORM_CLASS = 'cartridge.shop.forms.OrderForm' + +# If True, the checkout process is split into separate +# billing/shipping and payment steps. +# SHOP_CHECKOUT_STEPS_SPLIT = True + +# If True, the checkout process has a final confirmation step before +# completion. +# SHOP_CHECKOUT_STEPS_CONFIRMATION = True + +# Controls the formatting of monetary values accord to the locale +# module in the python standard library. If an empty string is +# used, will fall back to the system's locale. +# SHOP_CURRENCY_LOCALE = "" + +# Dotted package path and name of the function that +# is called on submit of the billing/shipping checkout step. This +# is where shipping calculation can be performed and set using the +# function ``cartridge.shop.utils.set_shipping``. +# SHOP_HANDLER_BILLING_SHIPPING = \ +# "cartridge.shop.checkout.default_billship_handler" + +# Dotted package path and name of the function that +# is called once an order is successful and all of the order +# object's data has been created. This is where any custom order +# processing should be implemented. +# SHOP_HANDLER_ORDER = "cartridge.shop.checkout.default_order_handler" + +# Dotted package path and name of the function that +# is called on submit of the payment checkout step. This is where +# integration with a payment gateway should be implemented. +# SHOP_HANDLER_PAYMENT = "cartridge.shop.checkout.default_payment_handler" + +# Sequence of value/name pairs for order statuses. +# SHOP_ORDER_STATUS_CHOICES = ( +# (1, "Unprocessed"), +# (2, "Processed"), +# ) + +# Sequence of value/name pairs for types of product options, +# eg Size, Colour. NOTE: Increasing the number of these will +# require database migrations! +# SHOP_OPTION_TYPE_CHOICES = ( +# (1, "Size"), +# (2, "Colour"), +# ) + +# Sequence of indexes from the SHOP_OPTION_TYPE_CHOICES setting that +# control how the options should be ordered in the admin, +# eg for "Colour" then "Size" given the above: +# SHOP_OPTION_ADMIN_ORDER = (2, 1) + + +###################### # MEZZANINE SETTINGS # ###################### @@ -21,7 +91,9 @@ from django.utils.translation import ugettext_lazy as _ # # ADMIN_MENU_ORDER = ( # ("Content", ("pages.Page", "blog.BlogPost", -# "generic.ThreadedComment", (_("Media Library"), "media-library"),)), +# "generic.ThreadedComment", (_("Media Library"), "fb_browse"),)), +# (_("Shop"), ("shop.Product", "shop.ProductOption", "shop.DiscountCode", +# "shop.Sale", "shop.Order")), # ("Site", ("sites.Site", "redirects.Redirect", "conf.Setting")), # ("Users", ("auth.User", "auth.Group",)), # ) @@ -247,6 +319,7 @@ INSTALLED_APPS = ( "mezzanine.core", "mezzanine.generic", "mezzanine.pages", + "cartridge.shop", "mezzanine.blog", "mezzanine.forms", "mezzanine.galleries", @@ -272,6 +345,7 @@ MIDDLEWARE_CLASSES = ( 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "cartridge.shop.middleware.ShopMiddleware", "mezzanine.core.request.CurrentRequestMiddleware", "mezzanine.core.middleware.RedirectFallbackMiddleware", "mezzanine.core.middleware.TemplateForDeviceMiddleware", diff --git a/config/urls.py b/config/urls.py index c67fd17..1b3b87c 100644 --- a/config/urls.py +++ b/config/urls.py @@ -8,6 +8,8 @@ from django.views.i18n import set_language from mezzanine.core.views import direct_to_template from mezzanine.conf import settings +from cartridge.shop.views import order_history + admin.autodiscover() @@ -27,6 +29,10 @@ if settings.USE_MODELTRANSLATION: ] urlpatterns += [ + # Cartridge URLs. + url("^shop/", include("cartridge.shop.urls")), + url("^account/orders/$", order_history, name="shop_order_history"), + # We don't want to presume how your homepage works, so here are a # few patterns you can use to set it up. @@ -93,7 +99,7 @@ urlpatterns += [ # Note that for any of the various homepage patterns above, you'll # need to use the ``SITE_PREFIX`` setting as well. - # ("^%s/" % settings.SITE_PREFIX, include("mezzanine.urls")) + # url("^%s/" % settings.SITE_PREFIX, include("mezzanine.urls")) ] diff --git a/requirements.txt b/requirements.txt index 0c4e494..4d43fbe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ Mezzanine==4.2.2 +cartridge==0.12.0 django-ses
追加した内容については、Mezzanine と Cartridge を同時にインストールするコマンドを叩いたときの settings.py の差分を参照しました(詳しくは後述)。
マイグレーションを実行します。
(akiyokoproject)$ python manage.py makemigrations No changes detected (akiyokoproject)$ python manage.py migrate System check identified some issues: WARNINGS: ?: (mysql.W002) MySQL Strict Mode is not set for database connection 'default' HINT: MySQL's Strict Mode fixes many data integrity problems in MySQL, such as data truncation upon insertion, by escalating warnings into errors. It is strongly recommended you activate it. See: https://docs.djangoproject.com/en/1.10/ref/databases/#mysql-sql-mode Operations to perform: Apply all migrations: admin, auth, blog, conf, contenttypes, core, django_comments, forms, galleries, generic, pages, redirects, sessions, shop, sites, twitter Running migrations: Applying shop.0001_initial... OK Applying shop.0002_auto_20141227_1331... OK Applying shop.0003_emailfield... OK Applying shop.0004_productimage_file_field... OK Applying shop.0005_auto_20150527_1127... OK Applying shop.0006_auto_20150916_0459... OK Applying shop.0007_auto_20150921_2323... OK
shop 関連のテーブルが作成されました。
以下の Cartridge のテンプレートを、templates/ および custom/ 配下にコピーします。
(例によって、admin 関連のテンプレートは修正しないため、コピー対象に含めていません。)
templates/accounts/account_profile_update.html templates/email/base.html templates/email/order_receipt.html templates/email/order_receipt.txt templates/email/receipt.html templates/email/receipt_rtl.html templates/pages/category.html templates/shop/base.html templates/shop/billing_shipping.html templates/shop/cart.html templates/shop/checkout.html templates/shop/complete.html templates/shop/confirmation.html templates/shop/includes/order_details.html templates/shop/includes/order_details_rtl.html templates/shop/includes/order_totals.html templates/shop/includes/order_totals.txt templates/shop/includes/payment_fields.html templates/shop/includes/user_panel.html templates/shop/order_history.html templates/shop/order_invoice.html templates/shop/order_invoice_pdf.html templates/shop/payment.html templates/shop/product.html templates/shop/wishlist.html custom/templates/accounts/account_profile_update.html custom/templates/email/base.html custom/templates/email/order_receipt.html custom/templates/email/order_receipt.txt custom/templates/email/receipt.html custom/templates/email/receipt_rtl.html custom/templates/pages/category.html custom/templates/shop/base.html custom/templates/shop/billing_shipping.html custom/templates/shop/cart.html custom/templates/shop/checkout.html custom/templates/shop/complete.html custom/templates/shop/confirmation.html custom/templates/shop/includes/order_details.html custom/templates/shop/includes/order_details_rtl.html custom/templates/shop/includes/order_totals.html custom/templates/shop/includes/order_totals.txt custom/templates/shop/includes/payment_fields.html custom/templates/shop/includes/user_panel.html custom/templates/shop/order_history.html custom/templates/shop/order_invoice.html custom/templates/shop/order_invoice_pdf.html custom/templates/shop/payment.html custom/templates/shop/product.html custom/templates/shop/wishlist.html
最後に、プロセスを再起動して完了です。
(akiyokoproject)$ sudo supervisorctl restart all
細かい話
2.1. ライブラリの差分
Cartridge 導入前のライブラリ一覧
awscli (1.11.10) beautifulsoup4 (4.5.1) bleach (1.4.3) boto (2.41.0) botocore (1.4.67) chardet (2.3.0) colorama (0.3.7) Django (1.10.3) django-appconf (1.0.2) django-compressor (2.0) django-contrib-comments (1.7.3) django-ses (0.8.0) docutils (0.12) filebrowser-safe (0.4.6) future (0.16.0) futures (3.0.5) grappelli-safe (0.4.5) gunicorn (19.6.0) html5lib (0.9999999) jmespath (0.9.0) Mezzanine (4.2.2) MySQL-python (1.2.5) oauthlib (2.0.0) Pillow (3.4.2) pip (8.1.2) psycopg2 (2.6.2) pyasn1 (0.1.9) python-dateutil (2.5.3) python-memcached (1.58) pytz (2016.7) rcssmin (1.0.6) requests (2.11.1) requests-oauthlib (0.7.0) rjsmin (1.0.12) rsa (3.4.2) s3transfer (0.1.9) setproctitle (1.1.10) setuptools (24.0.2) six (1.10.0) tzlocal (1.3) wheel (0.29.0)
導入後のライブラリ一覧
$ pip list awscli (1.11.10) beautifulsoup4 (4.5.1) bleach (1.4.3) boto (2.41.0) botocore (1.4.67) Cartridge (0.12.0) chardet (2.3.0) colorama (0.3.7) Django (1.10.3) django-appconf (1.0.2) django-compressor (2.0) django-contrib-comments (1.7.3) django-ses (0.8.0) docutils (0.12) filebrowser-safe (0.4.6) future (0.16.0) futures (3.0.5) grappelli-safe (0.4.5) gunicorn (19.6.0) html5lib (0.9999999) jmespath (0.9.0) Mezzanine (4.2.2) MySQL-python (1.2.5) oauthlib (2.0.0) Pillow (3.4.2) pip (8.1.2) psycopg2 (2.6.2) pyasn1 (0.1.9) PyPDF2 (1.26.0) python-dateutil (2.5.3) python-memcached (1.58) pytz (2016.7) rcssmin (1.0.6) reportlab (3.3.0) requests (2.11.1) requests-oauthlib (0.7.0) rjsmin (1.0.12) rsa (3.4.2) s3transfer (0.1.9) setproctitle (1.1.10) setuptools (24.0.2) six (1.10.0) tzlocal (1.3) wheel (0.29.0) xhtml2pdf (0.0.6)
2.2. config/setting.py の差分
Mezzanine と Cartridge を同時にインストールするには、
mezzanine-project -a cartridge config .
というコマンドを叩けばよいのですが、Mezzanine だけをインストールした場合と Mezzanine と Cartridge を同時にインストールした場合とで、自動生成される settings.py の差分を確認してみました。
1)(Cartridge 無しの)通常 Mezzanine インストール
sudo mkdir -p /opt/webapps/mezzproject sudo chown -R `whoami`. /opt/webapps cd /opt/webapps/mezzproject/ mezzanine-project config .
2)Cartridge 付きの Mezzanine インストール
sudo mkdir -p /opt/webapps/mezzproject_cartridge sudo chown -R `whoami`. /opt/webapps cd /opt/webapps/mezzproject_cartridge/ mezzanine-project -a cartridge config .
(diff 関連の参考リンク)
$ diff -r -q /opt/webapps/mezzproject /opt/webapps/mezzproject_cartridge $ diff -r -u /opt/webapps/mezzproject /opt/webapps/mezzproject_cartridge | vi -R - -q : ファイル名だけ表示 -r : ディレクトリ比較 -u : ユニファイド形式 | vi -R - : vim の機能を使って色分け
ちなみに、進んでいる方を第二引数にした方がよさそうです(+ で diff表示されるので)。
$ diff -r -u /opt/webapps/mezzproject /opt/webapps/mezzproject_cartridge Only in /opt/webapps/mezzproject_cartridge/config: dev.db diff -r -u /opt/webapps/mezzproject/config/local_settings.py /opt/webapps/mezzproject_cartridge/config/local_settings.py --- /opt/webapps/mezzproject/config/local_settings.py 2016-11-03 03:06:58.160419703 +0000 +++ /opt/webapps/mezzproject_cartridge/config/local_settings.py 2016-11-03 03:07:09.000000000 +0000 @@ -8,8 +8,8 @@ DEBUG = True # Make these unique, and don't share it with anybody. -SECRET_KEY = "k8dd+%#@kw6vh1a-#k(l1agb=@kibpbz&s$7nk)l@s06gz*gx9" -NEVERCACHE_KEY = "15fmx+kop%oxps(1w3uq5i1btc#3t=+t!#=ax@3!4^@$s%4gu2" +SECRET_KEY = "(#eotia6m9*m(9vt)@u7^6@)e$f44c(4)4n9*((o%(^u3!u&cw" +NEVERCACHE_KEY = "n66suh9!=@^-_6^toe)s_z)c$0g70e%in1ifzo7w%&uf&b#z(y" DATABASES = { "default": { diff -r -u /opt/webapps/mezzproject/config/settings.py /opt/webapps/mezzproject_cartridge/config/settings.py --- /opt/webapps/mezzproject/config/settings.py 2016-11-03 03:06:58.164419703 +0000 +++ /opt/webapps/mezzproject_cartridge/config/settings.py 2016-11-03 03:07:09.000000000 +0000 @@ -7,6 +7,76 @@ ###################### +# CARTRIDGE SETTINGS # +###################### + +# The following settings are already defined in cartridge.shop.defaults +# with default values, but are common enough to be put here, commented +# out, for conveniently overriding. Please consult the settings +# documentation for a full list of settings Cartridge implements: +# http://cartridge.jupo.org/configuration.html#default-settings + +# Sequence of available credit card types for payment. +# SHOP_CARD_TYPES = ("Mastercard", "Visa", "Diners", "Amex") + +# Setting to turn on featured images for shop categories. Defaults to False. +# SHOP_CATEGORY_USE_FEATURED_IMAGE = True + +# Set an alternative OrderForm class for the checkout process. +# SHOP_CHECKOUT_FORM_CLASS = 'cartridge.shop.forms.OrderForm' + +# If True, the checkout process is split into separate +# billing/shipping and payment steps. +# SHOP_CHECKOUT_STEPS_SPLIT = True + +# If True, the checkout process has a final confirmation step before +# completion. +# SHOP_CHECKOUT_STEPS_CONFIRMATION = True + +# Controls the formatting of monetary values accord to the locale +# module in the python standard library. If an empty string is +# used, will fall back to the system's locale. +# SHOP_CURRENCY_LOCALE = "" + +# Dotted package path and name of the function that +# is called on submit of the billing/shipping checkout step. This +# is where shipping calculation can be performed and set using the +# function ``cartridge.shop.utils.set_shipping``. +# SHOP_HANDLER_BILLING_SHIPPING = \ +# "cartridge.shop.checkout.default_billship_handler" + +# Dotted package path and name of the function that +# is called once an order is successful and all of the order +# object's data has been created. This is where any custom order +# processing should be implemented. +# SHOP_HANDLER_ORDER = "cartridge.shop.checkout.default_order_handler" + +# Dotted package path and name of the function that +# is called on submit of the payment checkout step. This is where +# integration with a payment gateway should be implemented. +# SHOP_HANDLER_PAYMENT = "cartridge.shop.checkout.default_payment_handler" + +# Sequence of value/name pairs for order statuses. +# SHOP_ORDER_STATUS_CHOICES = ( +# (1, "Unprocessed"), +# (2, "Processed"), +# ) + +# Sequence of value/name pairs for types of product options, +# eg Size, Colour. NOTE: Increasing the number of these will +# require database migrations! +# SHOP_OPTION_TYPE_CHOICES = ( +# (1, "Size"), +# (2, "Colour"), +# ) + +# Sequence of indexes from the SHOP_OPTION_TYPE_CHOICES setting that +# control how the options should be ordered in the admin, +# eg for "Colour" then "Size" given the above: +# SHOP_OPTION_ADMIN_ORDER = (2, 1) + + +###################### # MEZZANINE SETTINGS # ###################### @@ -21,7 +91,9 @@ # # ADMIN_MENU_ORDER = ( # ("Content", ("pages.Page", "blog.BlogPost", -# "generic.ThreadedComment", (_("Media Library"), "media-library"),)), +# "generic.ThreadedComment", (_("Media Library"), "fb_browse"),)), +# (_("Shop"), ("shop.Product", "shop.ProductOption", "shop.DiscountCode", +# "shop.Sale", "shop.Order")), # ("Site", ("sites.Site", "redirects.Redirect", "conf.Setting")), # ("Users", ("auth.User", "auth.Group",)), # ) @@ -243,6 +315,7 @@ "mezzanine.core", "mezzanine.generic", "mezzanine.pages", + "cartridge.shop", "mezzanine.blog", "mezzanine.forms", "mezzanine.galleries", @@ -251,6 +324,7 @@ # "mezzanine.mobile", ) + # List of middleware classes to use. Order is important; in the request phase, # these middleware classes will be applied in the order given, and in the # response phase the middleware will be applied in reverse order. @@ -267,12 +341,15 @@ 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "cartridge.shop.middleware.ShopMiddleware", "mezzanine.core.request.CurrentRequestMiddleware", "mezzanine.core.middleware.RedirectFallbackMiddleware", "mezzanine.core.middleware.TemplateForDeviceMiddleware", "mezzanine.core.middleware.TemplateForHostMiddleware", "mezzanine.core.middleware.AdminLoginInterfaceSelectorMiddleware", "mezzanine.core.middleware.SitePermissionMiddleware", + # Uncomment the following if using any of the SSL settings: + # "mezzanine.core.middleware.SSLRedirectMiddleware", "mezzanine.pages.middleware.PageMiddleware", "mezzanine.core.middleware.FetchFromCacheMiddleware", ) diff -r -u /opt/webapps/mezzproject/config/urls.py /opt/webapps/mezzproject_cartridge/config/urls.py --- /opt/webapps/mezzproject/config/urls.py 2016-11-03 03:06:58.160419703 +0000 +++ /opt/webapps/mezzproject_cartridge/config/urls.py 2016-11-03 03:07:09.000000000 +0000 @@ -4,10 +4,11 @@ from django.conf.urls.i18n import i18n_patterns from django.contrib import admin from django.views.i18n import set_language - from mezzanine.core.views import direct_to_template from mezzanine.conf import settings +from cartridge.shop.views import order_history + admin.autodiscover() @@ -27,6 +28,11 @@ ] urlpatterns += [ + + # Cartridge URLs. + url("^shop/", include("cartridge.shop.urls")), + url("^account/orders/$", order_history, name="shop_order_history"), + # We don't want to presume how your homepage works, so here are a # few patterns you can use to set it up. @@ -93,7 +99,7 @@ # Note that for any of the various homepage patterns above, you'll # need to use the ``SITE_PREFIX`` setting as well. - # ("^%s/" % settings.SITE_PREFIX, include("mezzanine.urls")) + # url("^%s/" % settings.SITE_PREFIX, include("mezzanine.urls")) ] Only in /opt/webapps/mezzproject_cartridge: .DS_Store
以上を踏まえて、内容を追加しました。
*1:Mezzanine 公式ページには「Ecommerce / Shopping cart module」と紹介されています。