Django開発~ショッピングカート構築編2~商品一覧作成②の続きです。
今回は、商品の詳細ページを作成しましょう。
<目次>
商品詳細ページの仕様
最初に商品詳細ページの仕様を固めましょう。
商品一覧の中から好きな商品をクリックすると詳細ページに遷移できるようにします。
その為に実装しなければならないことを書き出してみます。
①商品一覧から画像をクリックすると詳細ページに遷移する
②urlを商品毎の詳細ページ用URLに変更する
例)「localhost:8000/」 -> 「localhost:8000/product_name」
③商品詳細ページを作成する
それでは、Todoリストにしたがって、実装を開始しましょう。

テスト駆動開発に慣れてきたと思いますので、今回からはToddoリストを作成しています。
実装
テスト駆動開発を実践しつつ、実装していきましょう。
URLリンクのテスト
最初はテストコードからです。
まずは、URLリンクのテストを行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
app/shop/tests/test_views.py class ProductDetailTest(TestCase): def setUp(self): self.category = sample_category( name='Black Urban Cushion', slug='black-urban-cushion', description='This is a category for black urban cushion') self.product = sample_product( name='dog', slug='dog', description='Dogs are intelligent animals!', category=self.category, price=30.2, stock=30, available=True) def test_correct_urls(self): response = self.client.get(self.product.get_url()) assert response.status_code == 200 |
テストを実行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
make test ============================================================ FAILURES ============================================================= shop/tests/test_views.py::ViewTest::test_retrieve_products FAILED shop/tests/test_views.py::ProductDetailTest::test_correct_urls FAILEDDestroying test database for alias 'default'... _______________________________________________ ProductDetailTest.test_correct_urls _______________________________________________ self = <test_views.ProductDetailTest testMethod=test_correct_urls> def test_correct_urls(self): > response = self.client.get(self.product.get_url) E AttributeError: 'Product' object has no attribute 'get_url' self = <test_views.ProductDetailTest testMethod=test_correct_urls> shop/tests/test_views.py:95: AttributeError ============================================================= 2 failed, 4 passed, 2 warnings in 1.92 seconds ============================================================== |
2つエラーが出ました。ひとまず、「AttributeError: ‘Product’ object has no attribute ‘get_url’」の方を対処します。
get_urlは、商品のURLを取得するためのものなのですが、まだ実装していません。
models.pyを開きましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
app/shop/models.py from django.db import models from django.urls import reverse # new class ValidManager(models.Manager): def get_queryset(self): return super(ValidManager, self).get_queryset().filter(available=True) class Category(models.Model): .... class Product(models.Model): .... # new def get_url(self): return reverse('shop:product_detail', args=[self.slug]) |
これで、先ほどのエラーは無くなるはずです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
============================================================ FAILURES ============================================================= _______________________________________________ ProductDetailTest.test_correct_urls _______________________________________________ self = <test_views.ProductDetailTest testMethod=test_correct_urls> def test_correct_urls(self): response = self.client.get(self.product.get_url) > assert response.status_code == 200 E AssertionError: assert 404 == 200 E -404 E +200 response = <HttpResponseNotFound status_code=404, "text/html"> self = <test_views.ProductDetailTest testMethod=test_correct_urls> shop/tests/test_views.py:96: AssertionError -------------------------------------------------------- Captured log call -------------------------------------------------------- WARNING django.request:log.py:228 Not Found: /<bound method Product.get_url of <Product: dog>> =================================================== 1 failed, 2 passed in 1.25s =================================================== |
今度は、404エラーになりました。ページが見つからないようです。
shop/urls.pyに商品詳細ページへのURLを追加してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
app/shop/urls.py cat app/shop/urls.py from django.urls import path from . import views app_name = 'shop' urlpatterns = [ path('', views.all_products, name='all_product'), path('<slug:product_slug>/', views.product_detail, name='product_detail'), #new ] |
テストを実行します。
1 |
E AttributeError: module 'shop.views' has no attribute 'product_detail' |
テスト結果が変わりましたね。
views.pyにproduct_detail関数を追加しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
app/shop/views.py from django.shortcuts import render from shop.models import Product def all_products(request): products = Product.valid_objects.all() return render(request, 'shop/product_list.html', {'products': products}) # new def product_detail(request, product_slug): try: product = Product.objects.get(slug=product_slug) except Exception as e: raise e return render(request, 'shop/product_detail.html', {'product': product}) |
商品のslugから対象の商品データを取得した後、product_detail.htmlに商品データを返却する実装をしました。
テストを実行します。
1 2 3 4 5 6 |
make test /usr/local/lib/python3.7/site-packages/django/template/loader.py:19: TemplateDoesNotExist django.template.exceptions.TemplateDoesNotExist: shop/product_detail.html |
エラーが変わりました。
shop/product_detail.htmlを作成します。
1 |
touch app/shop/templates/shop/product_detail.html |
テストを実行します。
1 2 3 |
make test shop/tests/test_views.py::ProductDetailTest::test_correct_urls PASSED |
テストがパスしました。
これで、Todoリストの②を消すことができます。
①商品一覧から画像をクリックすると詳細ページに遷移する
②urlを商品毎の詳細ページ用URLに変更する
例)「localhost:8000/」 -> 「localhost:8000/product_name」
③商品詳細ページを作成する
商品一覧の修正
商品一覧に詳細ページへのリンクを張りましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
app/shop/templates/shop/product_list.html ... <div> <div> {% for product in products %} <div> <div> <!-- new --> <a href="{{product.get_url}}"><img src="{{product.image.url}}" alt="{{product.name}}"></a> <div> <h4>{{product.name}}</h4> <p>£{{product.price}}</p> </div> </div> </div> {% endfor %} </div> </div> {% endblock %} |
「localhost:8000」にアクセスし、画像をクリックして、商品詳細画面に遷移できたら成功です。
これで、Todoリストの①を消せます。
①商品一覧から画像をクリックすると詳細ページに遷移する
②urlを商品毎の詳細ページ用URLに変更する
例)「localhost:8000/」 -> 「localhost:8000/product_name」
③商品詳細ページを作成する
商品詳細ページの作成
最後に詳細ページを作成して終わりにしましょう。
先ほど作成したproduct_detail.htmlを開いて、詳細ページを作っていきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
app/shop/templates/shop/product_detail.html {% extends "base.html" %} {% load staticfiles %} {% block metadescription %} {{ product.description|truncatewords:155 }} {% endblock %} {% block title %} {{ product.name }} - Perfect Cushion Store {% endblock %} {% block content %} <div> <div> <p><a href="{% url 'shop:all_product' %}">Home</a>|<a href="{{ product.get_url }}">{{product.category}}</a></p> </div> <div> <br> <div> <div> <div> <img src="{{product.image.url}}" alt="{{product.name}}"> </div> </div> <div> <div> <h1>{{product.name}}</h1> <p>£{{product.price}}</p> <p>Product Description</p> <p>{{product.description}}</p> {% if product.stock <= 0 %} <p><b>Out of Stock</b></p> {% else %} <a href="">Add to Cart</a> {% endif %} </div> </div> </div> </div> </div> {% endblock content %} |
画面をリロードしてみてください。
※商品一覧が表示されている場合は、詳細ページに移動してください。
商品詳細ページが表示されたら成功です。

これで、Todoリストの最後の項目が完了しました。
①商品一覧から画像をクリックすると詳細ページに遷移する
②urlを商品毎の詳細ページ用URLに変更する
例)「localhost:8000/」 -> 「localhost:8000/product_name」
③商品詳細ページを作成する
残件対応
ずっと放置していたエラーの対処をしましょう。エラーは、次のようなメッセージを出していました。
1 |
ValueError: The 'image' attribute has no file associated with it. |
どうやら、前回、imageプロパティを追加した時にデグレが発生したようです。
以下の方法で回避してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
app/shop/tests/test_views.py # image="test.png"を追加 # Productを作成 def sample_product(name, slug, description, category, price, stock, available, image="test.png"): return Product.objects.create( name=name, slug=slug, description=description, category=category, price=price, stock=stock, available=available, image=image) |
エラー内容は、「imageプロパティに紐付くファイルが指定されていない」ことを意味していると思われるので、ダミーファイル(test.png)を指定しました。
test.pngは実体のないファイルであり、自分で用意する必要はありません。
テストを実行してみましょう。
1 2 3 |
make test ============= 6 passed, 2 warnings in 1.57 seconds ====== |
テストがパスしましたね^^
今回は、ここまでにしましょう。
参考書籍
次回
次回は、Bootstrapで画面を整えていこうと思います。
コメントを残す
コメントを投稿するにはログインしてください。