15. REST Serializers¶
God application programming style is to strictly separate of Models, Views and Controllers. In typical classic Django jargon, Views act as, what outsiders normally would denote a controller.
Therefore, all data exchange between the View and the Model must be performed in a serializable format, namely JSON. This allows us to use the same business logic for the server, as well as for the client. It also means, that we could create native mobile apps, which communicate with a web-application, without ever seeing a line of HTML code.
15.1. Every URL is a REST endpoint¶
Every URL which is part of part of django-SHOP, namely the product’s list and detail views, the cart and checkout views, the order list and detail views, they all are REST endpoints. What does that mean?
15.1.1. Catalog List View¶
Say, we are working with the provided demo shop, then the product’s list view is available at
http://localhost:8000/de/shop/ . By appending
?format=json to the URL, the raw data making up
our product list, is rendered as a JSON object. For humans, this is difficult to read, therefore
the Django Restframework offers a version which is more legible: Instead of the above, we invoke the
URL as http://localhost:8000/de/shop/?format=api . This renders the list of products as:
15.1.2. Catalog Detail View¶
By following a URL of a product’s detail view, say http://localhost:8000/de/shop/smart-phones/apple-iphone-5?format=api , one may check the legible representation such as:
15.1.3. Routing to these endpoints¶
Since we are using CMS pages to display the catalog’s list view, we must provide an apphook to attach it to this page. These catalog apphooks are not part of the shop framework, but must be created and added to the project:
from cms.app_base import CMSApp from cms.apphook_pool import apphook_pool class CatalogListApp(CMSApp): name = "Catalog List" urls = ['myshop.urls.catalog'] apphook_pool.register(CatalogListApp)
We now must add routes for all sub-URLs of the given CMS page implementing the catalog list:
from django.conf.urls import url from rest_framework.settings import api_settings from shop.rest.filters import CMSPagesFilterBackend from shop.views.catalog import (AddToCartView, CMSPageProductListView, ProductRetrieveView) from myshop.serializers import (ProductSummarySerializer, ProductDetailSerializer) urlpatterns = [ url(r'^$', CMSPageProductListView.as_view( serializer_class=ProductSummarySerializer, )), url(r'^(?P<slug>[\w-]+)$', ProductRetrieveView.as_view( serializer_class=ProductDetailSerializer, )), url(r'^(?P<slug>[\w-]+)/add-to-cart', AddToCartView.as_view() ), ]
184.108.40.206. Products List View¶
The urlpattern matching the regular expression
^$ routes onto the catalog list view class
shop.views.catalog.CMSPageProductListView passing in a special serializer class, for
myshop.serializers.ProductSummarySerializer. This has been customized to represent
our product models in our catalog templates. Since the serialized data now is available as a Python
template engine, as well as by the client using for instance AngularJS.
This View class, which inherits from
rest_framework.generics.ListAPIView accepts a list of
filters for restricting the list of items.
As we (ab)use CMS pages as categories, we somehow must assign them to our products. Therefore our
example project assigns a many-to-many field named
cms_pages to our Product model. Using this
field, the merchant can assign each product to one or more CMS pages, using the apphook
shop.rest.filters.CMSPagesFilterBackend, is responsible
for restricting selected products on the current catalog list view.
220.127.116.11. Product Detail View¶
The urlpattern matching the regular expression
^(?P<slug>[\w-]+)$ routes onto the class
shop.views.catalog.ProductRetrieveView passing in a special serializer class,
myshop.serializers.ProductDetailSerializer which has been customized to represent our
product model details.
This View class inherits from
rest_framework.generics.RetrieveAPIView. In addition to the
serializer_class it can accept these fields:
lookup_field: Model field to look up for the retrieved product. This defaults to
lookup_url_kwarg: URL argument as used by the matching RegEx. This defaults to
product_model: Restrict to products of this type. Defaults to
18.104.22.168. Add Product to Cart¶
The product detail view requires another serializer, the so called
serializer is responsible for controlling the number of items being added to the cart and gives
feedback on the subtotal of that potential cart item.
By appending the special string
add-to-cart to the URL of a product’s detail view, say
http://localhost:8000/de/shop/smart-phones/apple-iphone-5/add-to-cart?format=api , one may check
the legible representation of this serializer:
This serializer is slightly different than the previous ones, because it not only serializes
data and sends it from the server to the client, but it also deserializes data submitted from the
client back to the server using a post-request. This normally is the quantity, but in more
elaborated use cases, it also could contain attributes to distinguish product variations. The
AddSmartPhoneToCartSerializer for example, uses this pattern.
Since we may create our own Add this Product to Cart Serializer for each product type in our shop, hence overriding its functionality with a customized implementation, such a serializer may return any other information relevant to the customer. This could for instance be a rebate or just an update of the availability.
15.1.4. Cart and Checkout Views¶
CMS pages containing forms to edit the cart and the checkout views, do not require any URL routing, because their HTML is rendered by the CMS plugin system, whereas form submissions are handled by hard coded REST endpoints. These URLs are exclusively used by Ajax requests and never visible in the URL line of our browser. Those endpoints are configured by adding them to the root resolver at a project level:
urlpatterns = [ ... url(r'^shop/', include('shop.urls', namespace='shop')), ... ]
15.1.5. Order List and Detail Views¶
The Order List and Detail Views must be accessible through a CMS page, therefore we need a speaking
URL. This is similar to the Catalog List View. This means that the Order Views require the apphook
named “View Orders”, which must be configured in the advanced settings of the Order’s CMS pages.
This apphook is shipped with django-SHOP itself and can be found at
As with all other Views used by django-SHOP, the content of this View can also be rendered in
its dictionary structure, instead of HTML. Just append
?format=api to the URL and get the Order
details. In our myshop example this may look like:
15.1.6. Search Result Views¶
As with the Order View, also the Search Results View is accessible through a CMS page. Say, a
search query directed us to http://localhost:8000/en/search/?q=iphone , then the content of this
query can be made visible by adding
&format=api to this URL and get the results in its
dictionary structure. This is specially useful to test if a customized search serializer returns
the expected results. In our myshop example this may look like:
15.2. Final Note¶
In previous versions of django-SHOP, these kinds of controller implementations had to be implemented by customized Django View classes. This programming pattern led to bloated code, because the programmer had to do a case distinction, whether the request was of type GET, POST or some kind of Ajax. Now django-SHOP is shipped with reusable View classes, and the merchant’s implementation must focus exclusively on serializers. This is much easier, because it separates the business logic from the underlying request-response-cycle.