paint-brush
Building an Open-Source Python API (Tribal) Using the Django REST Framework - Part Threeby@strivecode
314 reads
314 reads

Building an Open-Source Python API (Tribal) Using the Django REST Framework - Part Three

by Abdul-Razak AlhassanMarch 16th, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

This article is the third in a series of articles on building and deploying an open-source grade REST API using the Django REST framework. In this section, we shall dive straight into coding our project. Before we do that, let us learn the second approach to setting up our Django REST API project.
featured image - Building an Open-Source Python API (Tribal) Using the Django REST Framework - Part Three
Abdul-Razak Alhassan HackerNoon profile picture

This article is the third in a series of articles. I am happy to give comprehensive tuition on building and deploying an open-source grade REST API using the Django REST framework. If you have not seen my first and second articles, kindly read the previous articles before continuing.


But if you feel confident in yourself, let us ride along. These are links to the first and second articles.


In this section, we shall dive straight into coding our project. But before we do that, let us learn the second approach to setting up our Django REST API project.

The Second Approach of Installing Dependencies

The second approach to setting up a Django REST API is great for production-grade applications. Because we intend to make this API production-grade and possibly open source, I will advise you to stick to this second approach.


Open the IDE or code editor of your choice. I am using Visual Studio Code. Make sure to set the python development environment on visual studio code.


Whilst at the project root directory, run the code below.


code . #Take note of the period


The advantage of this approach is that you will research the kind of libraries to include in your API before you begin coding. Follow the following steps to set up your project after creating the virtual-environment as we did before.


# Create a workspace inside the project workspace

$ touch tribal.code-workspace # For VScode environment setup

# Create the requirements.txt file and list all the dependencies of the project
# This makes it possible to install the listed dependencies at once.

$ touch requirements.txt


We know the initial dependencies we need for our API development, so let us list them in our requirements file. I also want us to install the latest long-term version of Django.


Because Django is in constant development, we must lock down the version to use for the project.


# Inside requirements.txt enter following dependencies
django>=3.2.0,<4.0
djangorestframework
pyyaml
requests
django-cos-headers
psycopg2-binary


Whilst at the root of the project folder and with the virtual environment activated, install the following requirements by the following command.


# Upgrade your pip installer
$ pip install --upgrade pip

# Install the dependencies
$ pip install -r requirements.txt

Creating a PostgreSQL Database

Now let's create the PostgreSQL database of our project on the terminal by running a few SQL commands. First, open the SQL shell on your terminal by entering the following command.


$ sudo -u postgres psql

# Create a new database at postgresql shell
postgres=# create database tribalapi;

# Create a user with and encrypted password
# Choose appropriate namem and password
postgres=# create user tribal with encrypted password 'password123';

# Now let's grant the new user all privileges to our database
postgres=# grant all privileges on database tribalapi to tribal;

Setting up a Django Project and Application

Now, let us set up our Django project and application and register our settings file. Navigate to the directory in which you created your virtual environment, and create the Django with the following command.


# Create Django project
$ django-admin start project tribalproject . # Don't forget to add the period


# Create Djanog app using python
$ python manage.py start triabalapp # Do not include a period

Configuring the Project Settings File

Now, we are set to develop our models, but before that, let us register our applications in the settings.py file in our project folder.


# Inside settingd.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'tribal_app',
]


After you have successfully installed PostgreSQL, let me show you how to set up Django to communicate with the database using a local host. In the settings.py file, scroll to the DATABASES dictionary, and make the following changes.


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


Do not hardcode your setup details as above, especially with a live project. You risk the security of your application if you hardcode your security details and host your code in an external repository web service like GitHub.


We shall deal with not hard-coding during the securing of the application process.

Models of the Tribal API

A Django model is a Python class that represents the structure and behavior of data stored in a database. They define the fields, relationships, and constraints that determine how data is stored and manipulated in the database.


In other words, a Django model defines the schema of a database table and provides an interface for accessing and manipulating the data stored in that table.


The following code defines each table as a class and makes each field attribute of the class. I used foreign keys to define relationships between tables.


from django.db import models

#Create your models here.

# This model translates our Regions table in the database
class Region(models.Model):
    name = models.CharField(max_length=255)
    country = models.ForeignKey('Country', on_delete=models.CASCADE)

    def __str__(self):
        return self.name

# This model translates our Countries table in the database
class Country(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()

    def __str__(self):
        return self.name

# This model translates our Tribes table in the database
class Tribe(models.Model):
    name = models.CharField(max_length=255)
    location = models.CharField(max_length=255)
    description = models.TextField()
    region = models.ForeignKey(Region, on_delete=models.CASCADE)

    def __str__(self):
        return self.name

# This model translates our Traditonal Healing table in the database
class HealingPractice(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    region = models.ForeignKey(Region, on_delete=models.CASCADE)

    def __str__(self):
        return self.name

# This model translates our Demorgraphy table in the database
class Demography(models.Model):
    tribe = models.ForeignKey(Tribe, on_delete=models.CASCADE)
    population = models.IntegerField()
    language = models.CharField(max_length=255)
    religion = models.CharField(max_length=255)

    def __str__(self):
        return f"{self.tribe} Demography"

# This model translates our Economic Activity table in the database
class EconomicActivity(models.Model):
    tribe = models.ForeignKey(Tribe, on_delete=models.CASCADE)
    activity = models.CharField(max_length=255)
    trade = models.CharField(max_length=255)
    description = models.TextField()

    def __str__(self):
        return f"{self.tribe} Economic Activity"

# This model translates our Environment table in the database
class Environment(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    region = models.ForeignKey(Region, on_delete=models.CASCADE)

    def __str__(self):
        return self.name

# This model translates our Traditional Authority table in the database
class TraditionalAuthority(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    tribe = models.ForeignKey(Tribe, on_delete=models.CASCADE)

    def __str__(self):
        return f"{self.tribe} Traditional Authority"

# This model translates our TribesRegion table in the database
class TribeRegion(models.Model):
    tribe = models.ForeignKey(Tribe, on_delete=models.CASCADE)
    region = models.ForeignKey(Region, on_delete=models.CASCADE)

    class Meta:
        unique_together = ('tribe', 'region')

    def __str__(self):
        return f"{self.tribe} - {self.region}"


A __str__method is defined for each model to help with debugging and viewing and returns a string representation of the model instance. This is used when displaying the object in the Django admin UI or other parts of the app.

Note that I added aunique_together constraint to the TribeRegion model metaclass, so that each tribe can only be associated with a region once.


You can use Django's built-in ORM to interact with these models and manipulate the data in your database. Below are the models of the database tables in our API.

Serializers of the Django REST API

The Django REST Framework serializer is a component of the Django REST Framework that provides a powerful toolkit for building web APIs.


Serializers are responsible for converting complex data types such as query sets and model instances into JSON or other content types over the internet.


In other words, you use serializers to convert data into formats that other systems and applications can consume. It also facilitates the validation of incoming data to ensure compliance with predefined rules and restrictions before further processing.


Overall, the Django REST Framework serializer is a tool for developing robust and scalable web APIs with Django, allowing easy and secure data transfer between various systems and applications.


from rest_framework import serializers
from .models import *

# This class serializes the Region model into JSON for easy transfere
class RegionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Region
        fields = '__all__'

# This class serializes the Country model into JSON for easy transfere
class CountrySerializer(serializers.ModelSerializer):
    class Meta:
        model = Country
        fields = '__all__'

# This class serializes the Tribes model into JSON for easy transfere
class TribeSerializer(serializers.ModelSerializer):
    region = RegionSerializer()

    class Meta:
        model = Tribe
        fields = '__all__'

# This class serializes the Traditional Healing model into JSON for easy transfere
class HealingPracticeSerializer(serializers.ModelSerializer):
    region = RegionSerializer()

    class Meta:
        model = HealingPractice
        fields = '__all__'

# This class serializes the Demography model into JSON for easy transfere
class DemographySerializer(serializers.ModelSerializer):
    tribe = TribeSerializer()

    class Meta:
        model = Demography
        fields = '__all__'

# This class serializes the Economic Activity model into JSON for easy transfere
class EconomicActivitySerializer(serializers.ModelSerializer):
    tribe = TribeSerializer()

    class Meta:
        model = EconomicActivity
        fields = '__all__'

# This class serializes the Environment model into JSON for easy transfere
class EnvironmentSerializer(serializers.ModelSerializer):
    region = RegionSerializer()

    class Meta:
        model = Environment
        fields = '__all__'

# This class serializes the Tradional Authority model into JSON for easy transfere
class TraditionalAuthoritySerializer(serializers.ModelSerializer):
    tribe = TribeSerializer()

    class Meta:
        model = TraditionalAuthority
        fields = '__all__'

# This class serializes the TribeRegion model into JSON for easy transfere
class TribeRegionSerializer(serializers.ModelSerializer):
    tribe = TribeSerializer()
    region = RegionSerializer()

    class Meta:
        model = TribeRegion
        fields = '__all__'


In the code above, I created a serializer class for each model. I used the ModelSerializer class provided by the Django REST Framework to generate serializer fields based on model fields.


Notice that the TribeSerializer, HealingPracticeSerializer, and EnvironmentSerializer classes contain nested serializers for the Region model. You can use these serializer classes in your Django REST framework views to serialize and deserialize data from your database.

Make Migrations

Now I want us to make migrations of models and serializers. This step is a quick way to determine if our models and serializers are bug-free and if we have a connection with the database.


Navigate to the project folder and issue the following commands at the terminal.

$ python manage.py makemigrations
$ python manage.py migrate

Conclusion

We are beginning to put shape to our API. The next part of our series will handle views and other technical details. We shall add OAuth and a third-party 0Auth2 to our API. Hopefully, we will begin to test the API in part four of our series using postman.


I hope you will enjoy the read.