Django REST’s Template Method Pattern and Other Patterns Plugged In
One of the main power of Django-REST is within its deep and broad (yet flexible) templating. This is, in fact, the main reason we chose Django-REST as our backend framework.
This article is written as a part of individual review criterion of PPL CS UI 2020
With this article, I will discuss how the Template Method Pattern by Django made our development way easier, even with the catches here and there. On top of that, we will see how to extend the Pattern and how it actually allows us to implement more patterns alongside it.
First thing first, the Template Method Pattern
Template Method is a behavioral design pattern that defines the skeleton of an algorithm in the superclass but lets subclasses override specific steps of the algorithm without changing its structure. — Refactoring Guru
Essentially, it is a way to provide the users the ability to customize each step of a transaction, without changing the sequence of the steps or the synchronization between each step. (You be good if you keep the Liskov Substitution Principle in mind).
Here is a good representation of the Template Method Pattern in its simplicity:
Mind you it is a simple representation. The thing is, you can extend the Abstract Class and the Concrete Class (which is not recommended, we should extend the Abstract Class instead) indefinitely.
And if you pay attention to it, you may come to the conclusion that it is logical for frameworks to have a lot of implementations of the Template Method Pattern. “It is a framework anyway, a framework is like a big template if you look at it from a certain angle.”
Template Method Pattern presence in Django REST
Django REST is quite generous with their templates. In fact, you can see its presence throughout the library-extended framework. Views, Serializers, Models, you name it, they are all templated.
The templatings are the reason that I sometimes refer some as “Django Magic”
Models
Well, a simple model won’t reveal much of its magic. As it turns out, the models have “hidden” methods, such as save()
and clean()
. The template suggests that before any object of any models, the clean()
method will be called before save()
. So if the data is not clean, i.e. violates some constraint, the save()
will not be conducted.
Awesome isn’t it? What this template does is it enforces the developers to apply the Single Responsibility Principle to the methods. How? By providing a template just for a single task, knowing that it is common to have constraints in our models.
Good news! You can even specify customization on the Abstract level as I said earlier! Below is another extension to the pattern:
And why would you want it? You can do this if you want to specify a custom method that will be used among many classes. Still, the concrete classes can further override it again as long as you do not violate the Liskov Substitution Principle.
Serializers
Serializers are the ones translating the object to JSON, and vice versa. And they can do things as well, such as validate the data give, etc. For example, you can override the validate()
method, the update()
, save()
, and many more just like in the models. One of our overriding is:
Furthermore, you can create a class specified to handle a certain type of object, implementing the Strategy Pattern. One of the common implementations is to create a custom bulk/list serializer to handle requests with multiple objects.
Views
Don’t even try to list the templates Django REST has given in views. There are a plethora of templates generously given by Django REST, each with their own specialty. I think, what they have done is trying to satisfy the needs of complex and common implementations.
Do you wish for a simple CRUD API? Just use a ModelViewSet, just say what the model is. Do you want a single endpoint for POST only? Here a CreateAPIView.
In my opinion, a silver-bullet implementation of the Template Method Pattern can be an overkill. Luckily, Django REST developers have the same mind. So, instead of creating one template that can do all things, Django REST provides many types of templates.
Essentially, they provide a template for just processing object creation, just deletion, update, or just retrieval alone, i.e. the RetrieveAPIView and all of its variation and combinations. What if you need more templated views? You can have a whole package in the APIView. Do you wish for a deeper level of model integration? ViewSet is your game. And so on. They are all templated to certain levels, some are more templated for certain usages than others.
For example, look at this viewset in particular. Look how much the template has done until the only thing we did is to customize how it deletes objects. All of the filterings, permissions, and even how to handle other operations are being handled by the template.
The main goal of templates is to provide customization if the users wish to have it. So, what if we need more custom methods? Just do as you wish.
Further, Django REST is generous enough to provide what they call Mixin, which are Abstract Classes handling certain operations only. Utilizing the Python ability of multi-parent subclassing, Django REST provides even wider range of options for customization. For example, if you need a nested update on your already templated view, you can extend the Viewset you wish to use and a NestedUpdateMixin.
Some big catches
As you might notice, the heavier the templating, the code gets more “invisible”. More flows are done in the background and not apparent to the eye of a sightseeing developer. This bear some certain risks:
- The implementation of some handlers/processes might result in different results than the developers expected. The code is invisible, the actual things done in the background might not exactly the one the developers expect. On top of that, you have to dig into the documentation to actually know the templating behind.
- Since the full flow is invisible, there is a chance of a wrong overriding on the templates. The chance of this happening increases with the number of variations in the templates.
- Over dependency on the templates, resulting in various templates being used instead of just 1 or 2 common templates. Some tendencies would appear to use so many variations of templates depending on the usages. This will result in a non-uniformity in the code.
The conclusion is, if possible, 1 or 2 more templates are better than tens of special ones, especially in big projects. The flows and connections within the whole system are more visible and readable. The same template is used in most of the places, and the codes are more explicitly elaborated.
Room for more patterns within the Template
As a template goes, it is just a series of methods arranged and customized in a neat way. That is it, nothing more.
The templates open it for more patterns to be plugged into the system.
For example, you can decide to plug a Factory Pattern to your system. Even the serializer is a Factory for the models, especially the ModelSerializer. The serializer plugs the value into a factory, and it returns an object.
Another example is a model factory of your own, you can use this to generate objects for testing, which is the common usage of it:
Another example is the Strategy Pattern, well one of the built-in implementations of this (orchestrated by the ViewSet template) is the permission classes on views:
Basically, it plugs a list of permission classes, then the template loops them all and checks for the permission of the requester. Cool isn’t it? It creates a Proxy Pattern by itself. Another one implementation of the Strategy Pattern is with the Object Manager Strategy, which is orchestrated by the models and the ViewSet through queryset parameter.
The object manager is a strategy that is being called mostly in the queries, it is used to set how we handle the queryset of a certain model. Even more, some of the Views templates define some querysets as the object of the view, which then calls these managers to query the objects.
Quite a template indeed…