Practical application of Singleton design pattern in Django
OUR Blog
PYTHON/DJANGO
May 04 2016

Practical application of Singleton design pattern in Django

Today there is a huge number of software development methodologies - TDD, ex, etc. Part of them refers to development process, another part to development management, and there are also methodologies defining which code to use in this or that case. One of these high-level methodologies is design pattern. This methodology is a set of some agreements and recommendations on code writing in certain situations, irrelevant to programming language.

To simplify the communication between developers, each recommendation has a name of its own - Singleton, Observer, etc. Such an approach is particularly useful, because irrespective of a language used by a programmer, an abstract task has an abstract solution clear for those familiar with Design Patterns.

In this article we are taking a look at one of design patterns, namely Singleton. Our purpose is to implement this pattern in python in order to use it in Django project.

So, what is Singleton?

The singleton pattern is a design pattern that restricts the instantiation of a class to one object

This is one of the most simple design patterns, but not the least useful. Most of the times it is used to coordinate the whole system. It can be a settings object, a connection object or session object, etc. What is important in this case is that at any moment we cannot have more than one Singleton object.

Let’s consider how this pattern can be used in django in practice. Settings for Web service, which will be stored in database and edited via admin panel, can be a practical example.

from django.db import models

class SingletonModel(models.Model):

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        self.pk = 1
        super(SingletonModel, self).save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        pass

    @classmethod
    def load(cls):
        obj, created = cls.objects.get_or_create(pk=1)
        return obj

This is a base class for Singleton model. When you call load method, an object will be loaded from a database or, if the object does not exist in a database, it will be created. So, in order to create a class responsible for site settings we will create a class based on an abstract SingletonModel.

class SiteSettings(SingletonModel):
    support = models.EmailField(default='supprot@example.com')
    sales_department = models.EmailField(blank=True)
    twilio_account_sid = models.CharField(max_length=255, default='ACbcad883c9c3e9d9913a715557dddff99')
    twilio_auth_token = models.CharField(max_length=255, default='abd4d45dd57dd79b86dd51df2e2a6cd5')
    twilio_phone_number = models.CharField(max_length=255, default='+15006660005')

To be able to edit settings we should register a model in django admin panel:

from django.contrib import admin
from .models import SingletonModel

admin.site.register(SingletonModel)

Now we can use created settings object in the following way:

from .models import SiteSettings

settings = SiteSettings.load()

When we use load method, an object will be taken from a database, and in case it was not created yet, it will be added to a database with default values. Thus, to get a working application from the start, we have to specify default values in settings or add blank=True, null=True attributes and to process such exceptions further.

To be able to use data from settings in the pattern, you can add an object of settings either in context of view or context processor.

context_processors.py

# -*- coding: utf-8 -*-

from __future__ import unicode_literals

from .models import SiteSettings


def settings(request):
    return {'settings': SiteSettings.load()}

Now let’s connect context process to settings.py:

TEMPLATES = [
    {
    ...

        'OPTIONS': {
        ...

                'context_processors': [
                    'common.context_processors.settings',
            ...
                ],
    },
]

After this we can use templates in the following way:

Support: {{ settings.support }}
{% if settings.sales_depatment %}
Sales Depatment: {{ settings.sales_depatment }}
{% endif %}

To reduce the amount of database requests you can save settings to cache. For this let’s add method set_cache to the model.

def set_cache(self):
    cache.set(self.__class__.__name__, self)

Let’s update save and load methods:

def save(self, *args, **kwargs):
        self.pk = 1
        super(SingletonModel, self).save(*args, **kwargs)
    self.set_cache()

def load(cls):
    if cache.get(self.__class__.__name__) is None:
        obj, created = cls.objects.get_or_create(pk=1)
        if not created:
            obj.set_cache()
        return cache.get(self.__class__.__name__)

As a result, we applied Singleton pattern for web application settings desing and storage, added settings to context processors, optimized settings as regards database requests using standard caching.

We received the answers on how exactly to implement edited settings via admin panel and how to solve such problems.

The final code is available on gist.github.

SIMILAR POSTS
Nov 29 2016
Create a project template with Vagrant, VirtualEnv and Ansible provisioner. And why is it necessary?
Nov 04 2016
Review of push notifications services and their functions. Read how to use push notifications effectively and make your app user-friendly in our article!
Sep 29 2016
This article will tell you why you need to keep Changelog. The history of the changelog management process on the example of python / django project using git instruments.