.. _tutorial/catalog-views:
=============
Catalog Views
=============
Now that we know how to create product models and how to administer them, lets have a look on how
to route them to our views.
When editing the CMS page used for the products list view, open **Advanced Settings** and choose
**Products List** from the select box labeled **Application**.
Then choose a template with at least one placeholder_. Click onto **View on site** to change into
front-end editing mode. Locate the main placeholder and add a **Row** followed by a **Column**
plugin from the section **Bootstrap**. Below that column add a **Catalog List Views** plugin from
section **Shop**. Then publish the page, it should not display any products yet.
.. _apphook: http://docs.django-cms.org/en/latest/how_to/apphooks.html
.. _placeholder: http://django-cms.readthedocs.org/en/latest/introduction/templates_placeholders.html#placeholders
Add products to the category
----------------------------
Open the detail view of a product in Django's administration backend. Locate the many-to-many
select box labeled **Categories > Cms pages**. Select the pages where each product shall appear
on.
On reloading the list view, the assigned products now shall be visible. Assure that they have been
set to be active, otherwise they won't show up.
If you nest categories, products assigned to children will be also be visible on their parents
pages.
Product Model Serializers
=========================
We already learned how to write model classes and model managers, so what are serializers for?
In **django-SHOP** the response views do not distinguish whether the product's information shall
be rendered as HTML or transferred via JSON. This gives us the ability to use the same business
logic for web browsers rendering static HTML, single page web applications communicating via AJAX
or native shopping applications for your mobile devices. This btw. is one of the great benefits
when working with RESTful_ API's and thanks to the djangorestframework_ we don't even have to
write any Django Views anymore.
For instance, try to open the list- or the detail view of any of the products available in the
shop. Then in the browsers URL input field append ``?format=api`` or ``?format=json`` to the URL.
This will render the pure product information, but without embedding it into HTML.
The REST API view is very handy while developing. If you want to hide this on your production
system , then in your settingy.py remove ``'rest_framework.renderers.BrowsableAPIRenderer'`` from
``REST_FRAMEWORK['DEFAULT_RENDERER_CLASSES']``.
In the shop's catalog, we need some functionality to render a list view for all products and
we need a detail view to render each product type. The **django-SHOP** framework supplies two
such serializers:
Serialize for the Products List View
------------------------------------
For each product we want to display in a list view, we need a serializer which converts the content
of the most important fields of a product. Normally these are the Id, the name and price, the URL
onto the detail view, a short description and a sample image.
The **django-SHOP** framework does not know which of those fields have to be serialized, therefore
it requires some help from the programmer:
.. code-block:: python
:caption: myshop/product_serializers.py
:linenos:
from shop.serializers.bases import BaseProductSummarySerializer
from myshop.models.polymorphic.product import Product
class ProductSummarySerializer(BaseProductSummarySerializer):
class Meta:
model = Product
fields = ('id', 'product_name', 'product_url',
'product_type', 'product_model', 'price')
All these fields can be extracted directly from the product model with the exception of the sample
image. This is because we yet do not know the final dimensions of the image inside its HTML element
such as ````, and we certainly want to resize it using PIL/Pillow before it is
delivered. An easy way to solve this problem is to use the ``SerializerMethodField``. Simply extend
the above class to:
.. code-block:: python
:linenos:
from rest_framework.serializers import SerializerMethodField
class ProductSummarySerializer(BaseProductSummarySerializer):
media = SerializerMethodField()
def get_media(self, product):
return self.render_html(product, 'media')
As you might expect, ``render_html`` assigns a HTML snippet to the field ``media`` in the serialized
representation of our product. This method uses a template to render the HTML. The name of this
template is constructed using the following rules:
* Look for a folder named according to the project's name, ie. ``settings.SHOP_APP_LABEL`` in lower
case. If no such folder can be found, then use the folder named ``shop``.
* Search for a subfolder named ``products``.
* Search for a template named “*label*-*product_type*-*postfix*.html”. These three subfieds are
determined using the following rule:
* *label*: the component of the shop, for instance ``catalog``, ``cart``, ``order``.
* *product_type*: the class name in lower case of the product's Django model, for instance
``smartcard``, ``smartphone`` or if no such template can be found, just ``product``.
* *postfix*: This is an arbitrary name passed in by the rendering function. As in the example
above, this is the string ``media``.
.. note:: It might seem “un-restful” to render HTML snippets by a REST serializer and deliver them
via JSON to the client. However, we somehow must re-size the images assigned to our product to
fit into the layout of our list view. The easiest way to do this in a configurable manner is
to use the easythumbnails_ library and its templatetag ``{% thumbnail product.sample_image ... %}``.
The template to render the media snippet could look like:
.. code-block:: django
:caption: myshop/products/catalog-smartcard-media.html
{% load i18n thumbnail djng_tags %}
{% thumbnail product.sample_image 100x100 crop as thumb %}
The template of the products list view then may contain a list iteration such as:
.. code-block:: django
:emphasize-lines: 5
{% for product in data.results %}