Programster's Blog

Tutorials focusing on Linux, programming, and open-source

Python - Abstract View

I have a really simple abstract-view package on packagist that allows me to
do some really basic templating and create some "view objects". A need arose to do a similar thing with Python, so here we go.

Steps

First, we created an AbstractVIew class. It appears that Python doesn't have a native concept for abstract classes, so we make use of the Abstract Base Classes (ABC) Python library.

AbstractView.py

from string import Template
from abc import ABC, abstractmethod


class AbstractView(ABC):

    @abstractmethod
    def getTemplateString(self):
        pass

    def __str__(self):
        return Template(self.getTemplateString()).substitute(self.__dict__)

As you can see, this is really just a case of running the Python3 string template functionality around an abstract method to get the template string. It substitutes the classe's properties into the template string before being returned. By overriding the __str__ method, we ensure this is what is returned when a user tried to print or get the string representation of our object.

MyView.py

Below is a really basic example of a view that makes use of the AbstractView class we just created. This represents an email you might be sending to someone. This is as easy as declaring our properties (making use of dataclasses so we don't have to write the constructor), and filling in the getTemplateString abstract method, to return the template.

from AbstractView import AbstractView
from dataclasses import dataclass

@dataclass
class MyView(AbstractView):
    rate: str
    name: str
    contact_email: str
    date_string: str


    def getTemplateString(self):
        return """Dear ${name},
We would like to inform you that
our rate is going to increase to £${rate} per hour on the ${date_string}.

If you have any questions or concerns, please email us at ${contact_email}.

Sincerely,
Harry"""

When executed with:

print(MyView("5.00", "Bob", "support@mydomain.com", "5th of November"))

... you get the following output:

Dear Bob,
We would like to inform you that
our rate is going to increase to £5.00 per hour on the 5th of November.

If you have any questions or concerns, please email us at support@mydomain.com.

Sincerely,
Harry

MyDockerComposeView.py

Sometimes, we our templates actually need to contain the ${xxx} pattern, and not be substituted. For example, if we wish to output a Docker Compose file that is expecting environment variables to be substituted at the last second. In such a case, we can escape the $ by using $$ like so:

from AbstractView import AbstractView
from dataclasses import dataclass

@dataclass
class MyDockerComposeView(AbstractView):
    registry: str
    project_name: str


    def getTemplateString(self):
        return """version: "3"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: app
    image: ${registry}/${project_name}
    restart: always
    environment:
      - FOO=$${FOO}
      - BAR=$${BAR}
  """

When executed with:

print(MyDockerComposeView("registry.mydomain.com", "project1"))

... you get the following output:

version: "3"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: app
    image: registry.mydomain.com/project1
    restart: always
    environment:
      - FOO=${FOO}
      - BAR=${BAR}

As you can see, the FOO and BAR environment variable declarations came out correctly, and will get substituted at the last second with Docker Compose.

main.py

For completeness, here is a main.py one can use to complete the codebase.

from MyView import MyView
from MyDockerComposeView import MyDockerComposeView

if __name__ == '__main__':
    myEmailView = MyView("5.00", "Bob", "support@mydomain.com", "5th of November")
    myDockerComposeFIle = MyDockerComposeView("registry.programster.org", "blog")
    print(myEmailView)

If you wish, you can download a zip file of the entire example codebase.

Last updated: 2nd March 2022
First published: 2nd March 2022