Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

Django e-commerce part27 Related Products

Related Productsの表示

models.py のproductmanagerに、get_relatedのfuncを追記する。

class ProductManager(models.Manager):
...
    def get_related(self, instance):
        return self.get_queryset()

views.py のProductdetailVIewのcontextを更新

related productsは、detailviewに表示するので、detailviewに追記する。 get_context_dataを上書きする。

class ProductDetailView(DetailView):
    model = Product
    def get_context_data(self, *args, **kwargs):
        context = super(ProductDetailView, self).get_context_data(*args, **kwargs)
        context["related"] = Product.objects.get_related()
        return context

views.py のtype errorを解消する

このままrunserverを実行すると、get_related() missing 1 required positional argument: 'instance' errorが表示されてしまう。この原因は、 def get_related(self, instance): self以外にあと1つinstanceの引数をとるため。そのため、instanceの引数をviews.pyで追記する。現時点では、get_relatedに、instanceを設定してないので、結果に影響はない。

class ProductDetailView(DetailView):
    model = Product
    def get_context_data(self, *args, **kwargs):
        context = super(ProductDetailView, self).get_context_data(*args, **kwargs)
        instance = self.get_object()
        print (instance)
        context["related"] = Product.objects.get_related(instance)
        return context
  • instance = self.get_object() に関しては、ProductDetailView().get_object() で、titleを返すようになっている。

ProductManagerとProductの関係について

  • Productで、objects = ProductManager() と挿入することによって、本来modelsに備わっている、Product.objects というメソッド?プロパティ?が上書きされる。そのため、ProductManagerに、get_relatedを定義しておくと、Product.objects.get_related()というメソッドをProductDetailViewで、利用することが可能。

models.py にget_relatedを追記していく

class ProductManager(models.Manager):
...
    def get_related(self, instance):
        products_one = self.get_queryset().filter(categories__in=instance.categories.all())
        products_two = self.get_queryset().filter(default=instance.default)
        qs = (products_one | products_two ).exclude(id=instance.id).distinct()
        return qs
...
  • products_one: instance.categories.all()は、ProductDetailView.get_objcet().categories.all()を想定している?結果的には、instanceに登録されているcategoriesが、含まれているかどうかで、filterをかけて、querysetをゲットしている。

  • products_twoは、defaultで、filterをかけている。

  • qs は、products_one + products_two - 自分 - 重複分を差し引いてリスト化 exclude(id=instance.id)で、selfがrelatedに含まれないようにしている。

product_detail.html を編集

product_list.htmlからコードをコピペして変数の箇所は修正しておく。

    {% for product in related %}
    <tr>
        <td><a href='{{ product.get_absolute_url }}'>{{ product.title }}</td>
    </tr>
    </br>
    {% endfor %

views.py のProductDetailViewに追記

  • orderの順番がランダムになる? order_by("?")を追記
context["related"] = Product.objects.get_related(instance).order_by("?")
  • 表示する件数を制限する。
context["related"] = Product.objects.get_related(instance).order_by("?")[:6]

上記の追記は、modelに追記でも可。ただ、表示に関することなので、viewに書いておいた方が無難かも。

models.py のProductにclass Metaを記載してみる。

class Product(models.Model):
...
    class Meta:
        ordering = ["-title"]

これで、基本的には、-titleで表示されるが、viewで、表示順を指定しておけば、modelの設定に関係なく表示を設定できる。