Adventures in Machine Learning

Building a RESTful API with Django and Django REST Framework: A Step-By-Step Guide

Introduction to Django REST Framework

Django REST Framework is a powerful tool that allows developers to build APIs quickly and easily. The framework is built on top of the Django web framework, which provides a solid foundation for developing web applications.

In this article, we’ll explore the benefits of using Django REST Framework and demonstrate how to set up a Django project with the REST Framework installed.

Benefits of using REST Framework

Django REST Framework has a number of benefits for developers. Here are a few of the most important ones:

  1. Serialization

    Django REST Framework provides powerful serialization capabilities that allow developers to easily convert complex data types like Python objects and models into JSON, XML, or other formats that can be easily transmitted and processed.

  2. Authentication

    The framework provides easy-to-use authentication methods that allow APIs to be secured against unauthorized access. Built-in support for OAuth2, token-based authentication, and session-based authentication makes it easy to add security to your APIs.

  3. Customizable and flexible

    Django REST Framework is highly customizable and flexible, which makes it easy to adapt to different needs. Whether you’re building a simple prototype or a complex enterprise application, you can tailor the framework to meet your specific requirements.

  4. Browsable APIs

    With Django REST Framework, you can provide browsable APIs that allow developers to interact with your API using their web browser.

    This can be particularly useful during development and testing, as it allows developers to easily explore and test the API.

  5. Documentation

    Django REST Framework provides powerful tools for generating API documentation, making it easy to keep your documentation up-to-date and accessible to developers.

Creating the puppies app

Now that we’ve explored some of the benefits of using Django REST Framework, let’s dive into setting up a Django project with the framework installed. For this tutorial, we’ll be creating an app called Puppies that will allow us to manage data about puppies.

First, let’s create a new Django project. Open your terminal or command prompt and navigate to the directory where you want to create the project.

Then, enter the following command:

django-admin startproject puppiesproject

This will create a new Django project called puppiesproject. Now, let’s create a new app within the project.

Enter the following command:

python manage.py startapp puppies

This will create a new app called puppies within your Django project.

Installing REST Framework

Next, let’s install Django REST Framework. To do this, open your terminal or command prompt and navigate to the directory where your Django project is located.

Then, enter the following command:

pip install djangorestframework

This will install Django REST Framework and all of its dependencies.

Now that we’ve installed Django REST Framework, we need to add it to our project’s settings.

Open the settings.py file in your project’s root directory and add the following code at the bottom of the file:

INSTALLED_APPS = [
    # ...
    'rest_framework',
    'puppies',
]

This will add Django REST Framework and our new puppies app to our project’s list of installed apps.

We also need to add some configuration options for Django REST Framework. Add the following code to your settings.py file:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ],
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
    ]
}

This code sets the default permissions to allow any user to access our API and sets the default renderer to JSON.

Conclusion

Django REST Framework is a powerful tool for building APIs. It provides a wide range of benefits to developers, including powerful serialization capabilities, easy authentication methods, and customizable and flexible options. By following the steps outlined in this article, you can quickly set up a Django project with the REST Framework installed and start building your own APIs.

Database and Model Setup

In the previous section, we saw how to create a new Django project and install Django REST Framework. In this section, we’ll continue building our Puppies app by setting up a Postgres database and defining a Puppy model.

Setting up Postgres database

PostgreSQL is a popular open-source database management system that provides high scalability and data integrity. To use PostgreSQL with Django, we need to install the psycopg2 library, which is a PostgreSQL adapter for Python.

First, make sure you have PostgreSQL installed and running on your computer. Next, open your terminal or command prompt and run the following command to install psycopg2:

pip install psycopg2

Next, we need to add the database configuration to our Django project’s settings. Open settings.py and add the following code:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'puppiesdb',
        'USER': 'puppiesadmin',
        'PASSWORD': 'mypassword',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

These settings tell Django to use the PostgreSQL database engine, and to connect to a database called puppiesdb with a username of puppiesadmin and a password of mypassword.

The database is assumed to be running locally on port 5432.

Note: It’s important to replace these values with their actual values in your specific setup.

Defining the Puppy model

Now that we have our database setup, we can define our Puppy model. Open puppies/models.py and add the following code:

from django.db import models

class Puppy(models.Model):
    name = models.CharField(max_length=50)
    age = models.IntegerField()
    breed = models.CharField(max_length=50)
    color = models.CharField(max_length=50)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

This model defines a Puppy with a name, age, breed, and color.

We’ve also added two timestamp fields, created_at and updated_at, which will automatically track when a Puppy is created and updated.

Sanity Check

Let’s do a quick sanity check to make sure everything is set up correctly. Open your terminal or command prompt and navigate to your project directory.

Then, enter the following command to create the database tables:

python manage.py migrate

This will create all of the necessary database tables based on the models we’ve defined.

Next, let’s verify that the puppies_puppy table has been created in the database.

Connect to your PostgreSQL database using the psql command:

psql -U puppiesadmin -d puppiesdb

This will connect to the puppiesdb database as the puppiesadmin user. At the psql prompt, enter the following command to see a list of tables:

dt

You should see a puppies_puppy table in the list.

Writing a unit test for the Puppy model

Now that we have our model and database set up, it’s time to write a unit test to verify that our code is working properly. Open puppies/tests.py and add the following code:

from django.test import TestCase
from .models import Puppy

class PuppyModelTest(TestCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        Puppy.objects.create(
            name='Fido',
            age=2,
            breed='Dalmatian',
            color='Black and White'
        )
    def test_puppy_name(self):
        puppy = Puppy.objects.get(id=1)
        expected_name = f'{puppy.name}'
        self.assertEquals(expected_name, 'Fido')
    def test_puppy_age(self):
        puppy = Puppy.objects.get(id=1)
        expected_age = f'{puppy.age}'
        self.assertEquals(expected_age, '2')
    def test_puppy_breed(self):
        puppy = Puppy.objects.get(id=1)
        expected_breed = f'{puppy.breed}'
        self.assertEquals(expected_breed, 'Dalmatian')
    def test_puppy_color(self):
        puppy = Puppy.objects.get(id=1)
        expected_color = f'{puppy.color}'
        self.assertEquals(expected_color, 'Black and White')

This test defines a new Testcase and creates a new Puppy for testing purposes in the setUpClass() method.

We then define four test cases, each of which tests one of the fields in the Puppy model.

To run this test, enter the following command in your terminal or command prompt:

python manage.py test puppies.tests.PuppyModelTest

This will run the test and output the results.

If everything is working properly, you should see all four tests pass.

Conclusion

In this section, we explored how to set up a Postgres database for our Django project and defined a Puppy model. We also did a quick sanity check to ensure that everything is working properly, and wrote a unit test to verify that our model is functioning correctly.

By following these steps, we now have a solid foundation for creating our Puppies app and building out its functionality.

Serializers

In the previous section, we saw how to set up a Postgres database and define a Puppy model. In this section, we’ll explore serializers, which are an essential part of building APIs with Django REST Framework.

Definition of serializers

Serializers allow us to convert complex data types (such as Django models) into JSON, XML, or other formats that can be easily transmitted and processed over HTTP. In other words, serializers are responsible for controlling the input and output of data from our APIs.

Django REST Framework provides two types of serializers: the Serializer class and the ModelSerializer class.

The Serializer class provides a flexible way to define and validate input and output data for a specific resource. The ModelSerializer class provides a shortcut for automatically generating serializers based on a model, saving lots of development time.

Defining a ModelSerializer for the Puppy model

We’ll be using the ModelSerializer class to define a serializer for our Puppy model. Open puppies/serializers.py and add the following code:

from rest_framework import serializers
from .models import Puppy

class PuppySerializer(serializers.ModelSerializer):
    class Meta:
        model = Puppy
        fields = ['id', 'name', 'age', 'breed', 'color', 'created_at', 'updated_at']

This code defines a new PuppySerializer class that inherits from the ModelSerializer class. We’ve specified the fields we want to include in the serialized output (the same fields as the Puppy model) and set the model to use to the Puppy model.

RESTful Structure

Now that we have a serializer for our Puppy model, we can start building out the structure of our API. In this section, we’ll explore RESTful APIs and endpoints, and define the URLs for our API.

Explanation of RESTful APIs and endpoints

RESTful APIs are designed to be stateless, meaning that each request is self-contained and contains all the information needed to fulfill that request. RESTful APIs are based on a set of principles, including using HTTP methods (such as POST and GET) to perform actions on resources, and using URLs to identify and locate resources.

Endpoints are the URLs that map to specific resources in our API. They specify the HTTP method to use (such as GET or POST) when accessing that resource.

An example of an endpoint for our Puppy resource might be:

GET /api/puppies/

This endpoint would return a list of all puppies in our database.

Defining the URLs for the API

Now that we understand RESTful APIs and endpoints, we can define the URLs for our own API. Open puppies/urls.py and add the following code:

from django.urls import path, include

from rest_framework import routers
from . import views

router = routers.DefaultRouter()
router.register(r'puppies', views.PuppyViewSet)

urlpatterns = [
    path('api/', include(router.urls)),
]

This code defines a new Django router and registers our Puppy viewset with the router.

The URLs for the router are then included under the ‘api/’ namespace.

We also need to define the viewset that will be used to retrieve data for the Puppy resource.

Add the following code to your views.py file:

from rest_framework import viewsets
from .models import Puppy
from .serializers import PuppySerializer

class PuppyViewSet(viewsets.ModelViewSet):
    queryset = Puppy.objects.all()
    serializer_class = PuppySerializer

This code defines a new viewset that inherits from the ModelViewSet class provided by Django REST Framework. We’ve set the queryset to fetch all puppies from the database and set the serializer class to our PuppySerializer.

Conclusion

In this section, we’ve explored serializers and their role in API development. We’ve defined a ModelSerializer for our Puppy model, which we then used to build out the structure of our API with RESTful principles.

By following these steps, we now have a fully functional API for managing Puppy resources in our Django project.

Routes and Testing (TDD)

In the previous section, we saw how to define the URLs for our API. In this section, we’ll explore using a test-first approach to building our API, and creating skeleton view functions for all routes.

Using a test-first approach

Test-driven development (TDD) is an approach to software development that emphasizes writing tests before writing code. With TDD, you write a failing test first, then write code to fulfill the requirements of the test.

This helps ensure that your code is correct and that it meets the requirements of the test.

Creating skeleton view functions for all routes

With TDD in mind, let’s create skeleton view functions for all the routes we defined in the previous section. Open puppies/views.py and add the following code:

from django.shortcuts import render
from django.http import JsonResponse

from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Puppy
from .serializers import PuppySerializer

@api_view(['GET'])
def api_overview(request):
    pass

@api_view(['GET'])
def puppy_list(request):
    pass

@api_view(['GET'])
def puppy_detail(request, pk):
    pass

@api_view(['POST'])
def puppy_create(request):
    pass

@api_view(['PUT'])
def puppy_update(request, pk):
    pass

@api_view(['DELETE'])
def puppy_delete(request, pk):
    pass

This code defines six view functions corresponding to the routes we defined earlier. We haven’t written any code yet – instead, we’re following a test-first approach where we’ll write tests to ensure that these functions work as expected.

Writing tests for creating a new Puppy record

Let’s start by writing tests to ensure that we can create a new Puppy record using our API. Open puppies/tests.py and add the following code:

from django.urls import reverse
from django.test import TestCase, Client

from rest_framework import status
from rest_framework.test import APITestCase, APIClient
from .models import Puppy
from .serializers import PuppySerializer

class PuppyTestCase(APITestCase):
    def setUp(self):
        self.client = APIClient()
        self.puppy_data = {'name': 'Fido', 'age': 2, 'breed': 'Dalmatian', 'color': 'Black and White'}
        self.response = self.client.post(reverse('puppy_create'), self.puppy_data, format='json')
    def test_create_puppy(self):
        self.assertEqual(self.response.status_code, status.HTTP_201_CREATED)
    def test_create_puppy_invalid(self):
        response = self.client.post(reverse('puppy_create'), {'name': 'Fido', 'age': 'not a number', 'breed': 'Dalmatian', 'color': 'Black and White'}, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

This code defines a new test case for creating a new Puppy record. We’ve set up a client and defined some dummy data for the new record.

We then send a POST request to the puppy_create URL with the data and ensure that the response status code is 201, indicating that the record was created successfully.

We’ve also included a second test case for creating an invalid Puppy.

Popular Posts