Microservice app with DRF — Part2

Describing developing micro services app based on DRF

Hasan Sajedi
6 min readNov 21, 2021

First small REST API Django setup as a microservices

In this part I would like to set out on how to set up a django project that can be used as a REST API microservice. An important disclaimer: the code presented here is to be used in a development environment, review security practices for the framework(s) when you want to use it in production or expose it to the internet.

1. Project structure

We will using the following stack:

Let’s first start with the project structure and create some files and folders. Create the following files and folders:

$ tree -L 1 --dirsfirst
.
├── config/
├── docker-compose.yml
├── Dockerfile
└── README.md

First update our Dockerfile:

$ cat DockerfileFROM python:3.8COPY . /prj/drfapp
RUN pip3 install -r /prj/drfapp/config/requirements.txt
CMD [ "gunicorn", "--config", \
"/prj/drfapp/config/gunicorn.py", \
"drfapp.wsgi" \
]

We will use a Python base image, and of course you can choose your own base image as you like. In the Dockerfile we’ll define that we need to copy the code, and install the Python package requirements. Lastly, we use Gunicorn as the WSGI application server.

Now, for convenience sake we’re using docker-compose to orchestrate our docker containers. So we will add the following contents to our docker-compose.yml file:

$ cat docker-compose.ymlversion: "3"volumes:
postgres:
driver: local
services:
postgres:
image: postgres:12.0
environment:
- POSTGRES_NAME=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=mysecretpassword
- PGDATA=pgdata
volumes:
- postgres:/var/lib/postgresql/data/pgdata
drfapp:
build:
context: .
ports:
- "2323:2323"
volumes:
- .:/prj/drfapp
depends_on:
- postgres

We will be using PostgreSQL as our datastore in this example, and we will calling our Django application drfapp.

Next, we need to add the configuration files in the folder config/, and we will add the following files:

$ ls config/
gunicorn.py requirements.txt

But first, let’s add the python packages we will using in the Django application, and we will be adding those in the requirements.txt file.

$ cat config/requirements.txtdjango==2.2.7
gunicorn==19.9.0
psycopg2-binary==2.8.4
djangorestframework==3.10.3

As you can see we will be using Django REST Framework for the REST API. Next up we will create the Django project.

2. Create the Django project

Now, that we have the basic structure set up. We need to create an actual Django project, and we will be using the Docker container for this. This way we don’t necessarily install Django on our host system.

First, build the drfapp container.

$ docker-compose build drfapp

Second, we will start an interactive shell in the drfapp container.

$ docker-compose run drfapp sh

Third, we will create the Django project by executing the following command in the attached shell:

# cd /prj/drfapp
# django-admin startproject drfapp

We’ve created the project ‘inside’ the container, and because we’ve attached a volume to the location of the files. So now we need to fix the file permissions on our host system, because they have been created as the root account. So, exit from the shell in the docker container, and change the owner of the folder that we’ve just created.

$ chown -R $USER:$USER drfapp/

3. Update settings.py

Because this will be a minimal Django implementation we will updating the settings.py file, and remove the non-essential elements.

  1. Update ALLOWED_HOSTS
ALLOWED_HOSTS = ["*"]

2. Update INSTALLED_APPS

INSTALLED_APPS = [     
'rest_framework',
'api',
]

3. Remove MIDDLEWARE, and its contents as we do not need it

4. Remove TEMPLATES, and its contents

5. Update the DATABASES dict

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': "postgres",
'USER': "postgres",
'PASSWORD': "mysecretpassword",
'HOST': "postgres",
'PORT': "5432",
}
}

6. Remove AUTH_PASSWORD_VALIDATORS, and its contents

7. Remove STATIC_URL, and its contents

8. Remove LANGUAGE_CODE, USE_I18N, USE_L10N, and its contents

9. Add the following Django REST Framework configuration

REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES": [
'rest_framework.renderers.JSONRenderer',
],
"DEFAULT_AUTHENTICATION_CLASSES": [],
"DEFAULT_PERMISSION_CLASSES": [],
"UNAUTHENTICATED_USER": None,
'PAGE_SIZE': 10,
'DEFAULT_PAGINATION_CLASS': \
'rest_framework.pagination.LimitOffsetPagination',
'DEFAULT_FILTER_BACKENDS': [
'rest_framework.filters.OrderingFilter'
],
}

10. Adding logging configuration, this is optional and you can edit/update it to your preferences.

LOGGING = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'verbose': {
'format':
'%(pathname)s:%(lineno)d (%(funcName)s) '
'%(levelname)s %(message)s',
},
'simple': {
'format': '%(levelname)s %(message)s'
},
},
'handlers': {
'null': {
'level': 'DEBUG', 'class': 'logging.NullHandler',
},
'stderr': {
'level': 'DEBUG',
'formatter': 'simple',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django': {
'handlers': ['stderr'],
'propagate': True,
'level': 'WARNING',
},
},
}

NOTE: Again, this is not a production ready configuration!

4. Configuring the Django api app

Now we are ready to create the Django app. We will call it api, and within it we will create a simple REST API. First, create a folder in which it will be contained.

$ mkdir drfapp/api/

Inside this folder create the following files:

$ ls drfapp/api/
__init__.py models.py serializers.py urls.py views.py

We will start with the file models.py, and we will create a very simple model.

# drfapp/api/models.py
from django.db import models
class Country(models.Model):
country_name = models.CharField(max_length=20)
local_currency = models.CharField(max_length=20)
added_on = models.DateTimeField(auto_now_add=True)

def __str__(self):
return self.country_name

DRF Serializers

DRF serializers convert Django data types, such as querysets, into a format that can be rendered into JSON or XML. For this app, you only need to create the Country serializer. In the currency_country app, create a file named serializers.py and add the code block below.

# drfapp/api/serializers.py
from rest_framework import serializers
from .models import Country
class CountrySerializer(serializers.ModelSerializer):
class Meta:
model = Country # this is the model that is being serialized
fields = ('country_name', 'local_currency')

Next, update the views.py file where we will add the Django Rest Framework ViewSet.

# drfapp/api/views.py
from rest_framework import viewsets
from .models import Country
from .serializers import CountrySerializer
class CountryViewSet(viewsets.ModelViewSet):
queryset = Country.objects.all()
serializer_class = CountrySerializer

Update the urls.py file, and we will use the Django Rest Framework default routers.

# drfapp/api/urls.py
from django.conf.urls import include, url
from rest_framework.routers import DefaultRouter
from api import viewsrouter = DefaultRouter()
router.register(r'countries', views.CountryViewSet)
urlpatterns = [url(r'^', include(router.urls))]

Also update the urls.py file, of the Django project itself and remove the paths to the admin environment.

# drfapp/urls.py
from django.conf.urls import url, include
urlpatterns = [
url(r'^', include('api.urls')),
]

5. Configuring Gunicorn

We will be using Gunicorn as the WSGI application server. Create and update the gunicorn.py file with the following content. Refer to the configuration documentation for more configuration options.

# config/gunicorn.py# Documentation at: http://docs.gunicorn.org/en/latest/index.html
django_project_name = "drfapp"
# Chdir to specified directory before apps loading
chdir = "/prj/drfapp/drfapp"
# The socket to bind to
bind = ":2323"
# The class of worker processes for handling requests
worker_class = "sync"
# Is a number of OS processes for handling requests
workers = 4
# Is a maximum count of active greenlets grouped in a pool that will be
# allowed in each process
worker_connections = 1000
# The maximum number of requests a worker will process before restarting
max_requests = 5000
# Workers silent for more than this many seconds are killed and restarted
timeout = 120
# Set environment variable
raw_env = \
["DJANGO_SETTINGS_MODULE={}.settings".format(django_project_name)]
# The access log file to write to, "-" means to stderr
accesslog = "-"
# The error log file to write to, "-" means to stderr
errorlog = "-"

6. Start it up

Before we can run the application, we need to make initial migrations and propagate the model definition into our database schema. We will be using our Docker container to create those migrations, and again as a result we need to change the folder permissions in the end.

# First start the postgresql container
$ docker-compose up -d postgres
# Create the migrations
$ docker-compose run drfapp \
/prj/drfapp/drfapp/manage.py \
makemigrations api
# Actually migrate the schema
$ docker-compose run drfapp \
/prj/drfapp/drfapp/manage.py \
migrate
# Reset the folder permissions of the created migrations folder
$ chown -R $USER:$USER drfapp

With that done we’re able to run our application, since we already started our postgresql container we will able to execute the following command.

$ docker-compose up drfapp

Now, the application should be running and you’ll be able to access the API with curl or any other method.

$ curl http://localhost:2323/countries/

Part 1:

https://hasansajedi.medium.com/microservice-app-with-drf-part1-71d54fe506a

--

--