#はじめに
django-oscar入門の第三回目です。
oscarでカスタマイズを繰り返せば、私が開発したOriginallyTeeのような、C2CのECサイトを作ることまでできます。
例 カタログのカスタマイズ
前回はシンプルなカスタマイズを行いました。もう少し複雑な例も説明します。
モデル、テンプレート、ビューに横断するカスタマイズを行います。
以下のドキュメント+αで説明します。
catalogueアプリのフォーク
フォークの方法は前回と同じです。
$ python manage.py oscar_fork_app catalogue yourappsfolder
Creating package yourappsfolder\catalogue
Creating admin.py
Creating app config
Creating models.py
Creating migrations folder
Replace the entry 'oscar.apps.catalogue.apps.CatalogueConfig' with 'yourappsfolder.catalogue.apps.CatalogueConfig' in INSTALLED_APPS
settings.pyのINSTALLED_APPSを修正します。
INSTALLED_APPS = [
#...
# 'oscar.apps.catalogue.apps.CatalogueConfig',
'yourappsfolder.catalogue.apps.CatalogueConfig', #追加
モデル(models.py)のカスタマイズ
商品ページに動画リンクを追加したいケースとします。商品モデルへvideo_urlフィールドを追加します。
PracticeOscar\Lib\site-packages\oscar\apps\catalogue\abstract_models.py
のAbstractProductに、商品のモデル発見しました。
AbstractProductにフィールド追加するため、models.pyを書き換えます。
from django.db import models
from oscar.apps.catalogue.abstract_models import AbstractProduct
class Product(AbstractProduct):
video_url = models.CharField(max_length=128, blank=True)
from oscar.apps.catalogue.models import *
Djangoのモデルのコードですが
以下の2点に注意して下さい。
1. oscar.apps.catalogue.abstract_models.pyからインポートする
2. oscar.apps.catalogue.models.pyのインポートを「最後」にする
-
oscarはabstract_models.pyでモデルを定義して、models.pyでそのモデルを読み込みます。よってabstract_models.pyをインポートして上書きします
-
同名のモデルが存在する場合は、先に存在するモデルのみ読み込みます。よってこちらで上書きしたモデルを先に読み込むため、models.pyのインポートを最後にしています
モデル更新をデータベースに反映させます。
$ python manage.py makemigrations catalogue
$ python manage.py migrate
python manage.py runserver
でサーバーにアクセスして、admin/catalogue/product/を見ると、商品モデルに新しいフィールドが追加されていますね。
※こちらはdjangoのデフォルトの管理画面で、oscarの管理画面のダッシュボードとは違うページなことに注意してください。両方使います。
せっかくなので 適当なリンクを記入して保存しておきましょう。
テンプレート(.html)のカスタマイズ
先のリンクを商品名の下に表示したいケース考えます。
テンプレートファイルの上書きは
- 同名のテンプレートファイルを作成してから
- djangoのテンプレート継承を行います
ここもフレームワーク側での該当するコード探しからスタートです。
テンプレートファイルは、仮想環境名PracticeOscarのとき
PracticeOscar\Lib\site-packages\oscar\templates\oscar
以下に存在します。これ以下のディレクトリはアプリによって分割されていますので、商品のテンプレートはcatalogue以下を探します。
PracticeOscar\Lib\site-packages\oscar\templates\oscar\catalogue\
さらに今回の修正は、商品の詳細ページの修正なので
PracticeOscar\Lib\site-packages\oscar\templates\oscar\catalogue\detail.html
の修正であることが分かります。
テンプレートの配置が分かったので、このディレクトリ構成を開発環境でも再現します。
ルート直下にtemplatesディレクトリを作成します。templates以下に修正したい全てのテンプレートを配置することになります。
そしてtemplates以降がフレームワーク側と同じになるように作成してください。
$ mkdir -p templates/oscar/catalogue
$ touch templates/oscar/catalogue/detail.html
以下はdjangoのテンプレート継承のコードとなります。
今回は{% block product_main %}{% endblock %}内の元のコードにaタグを追加することで、コードが変更されています。
この仕組みにより、少ないコード量で上書きできます。
{% extends "oscar/catalogue/detail.html" %}
{% load history_tags %}
{% load currency_filters %}
{% load reviews_tags %}
{% load product_tags %}
{% load display_tags %}
{% load i18n %}
{% load purchase_info_tags %}
{% block product_main %}
<div class="col-sm-6 product_main">
{% comment %}
This is a bit clunky here. Better to have some kind of JS-driven dashboard menu that
pops out when clicked. A bit like the Django-Debug-Toolbar button
{% endcomment %}
{% if user.is_staff %}
<a class="float-right d-none d-md-block" href="{% url 'dashboard:catalogue-product' pk=product.id %}">
<small><i class="fas fa-pencil-alt"></i> {% trans "Edit this product" %}</small>
</a>
{% endif %}
<h1>{{ product.get_title }}</h1>
<a href="{{ product.video_url }}" alt="youtube" target="_blank" rel="noopener noreferrer">動画リンク</a>
{% block product_stock_record %}
{% include "oscar/catalogue/partials/stock_record.html" with verbose=1 %}
{% endblock %}
{% iffeature "reviews" %}
{% include "oscar/catalogue/reviews/partials/review_stars.html" %}
{% endiffeature %}
<hr/>
{% if not product.is_parent %}
{% block product_basket_form %}
{% include "oscar/catalogue/partials/add_to_basket_form.html" %}
{% endblock %}
{% else %}
{% block variants %}
<h2>{% trans 'Variants:' %}</h2>
{% for child in product.children.public %}
{% purchase_info_for_product request child as child_session %}
{% if child_session.availability.is_available_to_buy %}
<a href="{{ child.get_absolute_url }}">{{ child.get_title }}</a><br>
{% endif %}
{% endfor %}
{% endblock %}
{% endif %}
</div><!-- /col-sm-6 -->
{% endblock %}
以上がテンプレートの継承になります。
まとめると
1. フレームワーク側でテンプレートファイルを見つける
2. 開発環境側のtemplatesディレクトリ以下に、ディレクトリ構成が同じになるようにテンプレートファイル設置する
3. djangoのテンプレート継承で、上書きしたい部分のみコードを修正する
3.はdjangoの機能ですので、今回もカスタマイズしたい部分のコードを素早く見つけられるかが重要となります。
ビュー(views.py)のカスタマイズ
リンクが存在するときのみ、リンクテキストを表示することを考えます。
商品詳細のビューの上書きを考えます。今回も該当のコード探しからスタートです。
PracticeOscar\Lib\site-packages\oscar\apps\catalogue\views.py
のProductDetailViewに、ビューがあることを見つけました。
views.pyを作成します。
$ touch yourappsfolder\catalogue\views.py
ProductDetailViewを修正するため、views.pyのget_context_dataを以下のように書き換えます。
djangoのクラスビューのメソッド、get_context_dataを上書きする典型処理です。
from oscar.core.loading import get_class
CoreProductDetailView = get_class("catalogue.views", "ProductDetailView")
class ProductDetailView(CoreProductDetailView):
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
if ctx["product"].video_url:
ctx["video_name"] = "動画リンクはこちら"
else:
ctx["video_name"] =""
return ctx
先のテンプレートのaタグも修正します。
{% comment %}
<a href="{{ product.video_url }}" alt="youtube" target="_blank" rel="noopener noreferrer">動画リンク</a>
を以下のように修正
{% endcomment %}
<a href="{{ product.video_url }}" alt="youtube" target="_blank" rel="noopener noreferrer">{{ video_name }}</a>
これでリンクの表示が動的に変更されるようになりました。
次回予定
今回はモデル(models.py)、テンプレート(.html)、ビュー(views.py)のカスタマイズ方法を説明しました。
同様の方法でフォーム(forms.py)等の他のファイルのカスタマイズもできますので、大半のカスタマイズはできるようになりました。
次はurlディスパッチャのカスタマイズとなります。
便利なWebサイト
github
https://github.com/django-oscar/django-oscar
ドキュメント 最初のうちはここを見ることが一番多いと思います
https://django-oscar.readthedocs.io/en/latest/
フォーラム 過去の質問を見ることもできます
https://groups.google.com/g/django-oscar
https://app.slack.com/client/T1QSP0999/C1QUF61T3
oscarはdjangoで作られているので、開発時はこちらも参照します
https://github.com/django/django
https://docs.djangoproject.com/en/5.0/