Stock Rest API

A rest API for realtime stock management built on the top of the Django Rest Framework with token authentication.

Posted by Praveen Chaudhary on 29 August 2020

Topics -> djangorestframework, python, webdevelopment, API

Source Code Link -> GitHub

What We are going to do?

  1. Starting the stock_rest_api django Project
  2. Creating a companies app within the stock_rest_api Project
  3. Create a Stock in companies/models.py
  4. Writing serializers for companies model data
  5. Creating a view for handling the request made from the client
  6. Adding function handlers to routes

Understanding Some Important Concepts

What is Django Framework?

Django is a Python-based free and open-source web framework that follows the model–template–views architectural pattern.

Top Features of Django Framework

  • Excellent Documentation
  • SEO Optimized
  • High Scalability
  • Versatile in Nature
  • Offers High Security
  • Provides Rapid Development

Django REST framework ?

Django REST framework is a powerful and flexible toolkit for building Web APIs.

Some reasons you might want to use REST framework:

Step 1 => Starting the Django Project

Initialize a Django project by following command. Python must be installed on your system.

pip install Django

You can confirm the installation by checking the django version by following command

python -m django --version
                        

Starting the Project

django-admin startproject stock_rest_api
                        

You get the project structure like this

stock_rest_api/
    manage.py
    stock_rest_api/
        __init__.py
        settings.py
        urls.py
        asgi.py
        wsgi.py
                        

Step 2 -> Creating a companies app within the stock_rest_api Project

What is a Django App?

An app is a Web application that does something – e.g., a Weblog system, a database of public records or a small poll app.

A project is a collection of configuration and apps for a particular website. A project can contain multiple apps. An app can be in multiple projects.

Creating the companies app

python manage.py startapp companies
                        

That’ll create a directory companies, which is laid out like this:

companies/
    __init__.py
    admin.py
    apps.py
    migrations/
        __init__.py
    models.py
    tests.py
    views.py

Including your app and libraries in project

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

    # framework for making rest api
    'rest_framework',

    # our main reusable components
    'companies.apps.CompaniesConfig',

    # authentication
    'rest_framework.authtoken',
]
                        

Step 3 -> Create a Stock model in companies/models.py

What is a Django Model?

A model is the single, definitive source of truth about your data. It contains the essential fields and behaviors of the data you’re storing. Django follows the DRY Principle.

The goal is to define your data model in one place and automatically derive things from it.

Let's create a Django Model.

A database contains a number of variable which are represented by fields in django model.Each field is represented by an instance of a Field class – e.g., CharField for character fields and DateTimeField for datetimes. This tells Django what type of data each field holds.

from django.db import models


class Stock(models.Model):
    company_name = models.CharField(max_length=10)
    open_price = models.FloatField()
    close_price = models.FloatField()
    transaction = models.IntegerField()

    def __str__(self):
        return self.company_name
                        

Adding models to admin panel

Django provides built-in admin panel to manage the data into model

from django.contrib import admin
from .models import Stock

# Register your models here.
admin.site.register(Stock)
                        

Making migrations

Once the model is defined, the django will automatically take schemas and table according to the fields supplied in the django model.

python manage.py makemigrations
python manage.py migrate
                        

Step 4 -> Writing serializers for companies model data

What are Serializers?

Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into JSON, XML or other content types. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.

We are using Model Serializer. But Why?

The ModelSerializer class provides a shortcut that lets you automatically create a Serializer class with fields that correspond to the Model fields.

The ModelSerializer class is the same as a regular Serializer class, except that:

  • It will automatically generate a set of fields for you, based on the model.
  • It will automatically generate validators for the serializer, such as unique_together validators.
  • It includes simple default implementations of .create() and .update().
from rest_framework import serializers
from rest_framework.serializers import Serializer
from .models import Stock
from rest_framework import exceptions
from django.contrib.auth import authenticate
from django.contrib.auth.models import User


class StockSerializer(serializers.ModelSerializer):
    class Meta:
        model = Stock
        # for some specific fields = ('companies','open_price')
        fields = '__all__'


class LoginSerializer(serializers.Serializer):
    username = serializers.CharField()
    password = serializers.CharField()

    def validate(self, data):
        username = data.get("username", "")
        password = data.get("password", "")

        if username and password:
            user = authenticate(username=username, password=password)
            if user:
                if user.is_active:
                    data["user"] = user
                else:
                    msg = "user is inactive"
                    raise exceptions.ValidationError(msg)
            else:
                msg = "Wrong credentials"
                raise exceptions.ValidationError(msg)
        else:
            msg = "Must provide username and password both"
            raise exceptions.ValidationError(msg)
        return data
                        

Here we are using ModelSerializer for Stock model and Default Serializer for user authentication

Default Serializer has a built-in validate function for validating the data entered

Step 5 -> Creating a view for handling the request made from the client.

What is a Django View?

A view function, or view for short, is a Python function that takes a Web request and returns a Web response.

Http Methods

HTTP Verb CRUD Entire Collection (e.g. /customers) Specific Item (e.g. /customers/{id})
POST Create 201 (Created), 'Location' header with link to /customers/{id} containing new ID. 404 (Not Found), 409 (Conflict) if resource already exists..
GET Read 200 (OK), list of customers. Use pagination, sorting and filtering to navigate big lists. 200 (OK), single customer. 404 (Not Found), if ID not found or invalid.
PUT Update/Replace 405 (Method Not Allowed), unless you want to update/replace every resource in the entire collection. 200 (OK) or 204 (No Content). 404 (Not Found), if ID not found or invalid.
PATCH Update/Modify 405 (Method Not Allowed), unless you want to modify the collection itself. 200 (OK) or 204 (No Content). 404 (Not Found), if ID not found or invalid.
DELETE Delete 405 (Method Not Allowed), unless you want to delete the whole collection—not often desirable. 200 (OK). 404 (Not Found), if ID not found or invalid.

companies/views.py

from django.contrib.auth import login, logout
from rest_framework.authentication import SessionAuthentication, BasicAuthentication, TokenAuthentication
from rest_framework.authtoken.models import Token
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.response import Response
from rest_framework.views import APIView

from .models import Stock
from .serializers import StockSerializer, LoginSerializer

class StockApiView(APIView):
    def get(self, request):
        stocks = Stock.objects.all()
        serializer = StockSerializer(stocks, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

    def post(self, request):
        data = request.data
        serializer = StockSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class StockApiViewDetail(APIView):
    authentication_classes = [BasicAuthentication, TokenAuthentication]
    permission_classes = [IsAuthenticated, IsAdminUser]

    def get_object(self, pk):
        try:
            return Stock.objects.get(pk=pk)
        except Stock.DoesNotExist as e:
            return Response({e: "given company stock not found."}, status=404)

    def get(self, request, pk=None):
        instance = self.get_object(pk)
        serializer = StockSerializer(instance)
        return Response(serializer.data, status=status.HTTP_200_OK)

    def put(self, request, pk=None):
        instance = self.get_object(pk)
        data = request.data
        serializer = StockSerializer(instance, data=data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        instance = self.get_object(pk)
        instance.delete()
        return HttpResponse(status=status.HTTP_204_NO_CONTENT)

class LoginApiView(APIView):

    def post(self, request):
        print(request.user)
        serializer = LoginSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data["user"]
        login(request, user)
        token, created = Token.objects.get_or_create(user=user)
        return Response({"token": token.key}, status=200)


class LogoutApiView(APIView):
    authentication_classes = (SessionAuthentication,)

    def post(self, request):
        logout(request)
        return Response(status=204)
                        

We are using Django Response in-built function.

Arguments:

  • data: The serialized data for the response.
  • status: A status code for the response. Defaults to 200. See also status codes.
  • template_name: A template name to use if HTMLRenderer is selected.
  • headers: A dictionary of HTTP headers to use in the response.
  • content_type: The content type of the response. Typically, this will be set automatically by the renderer as determined by content negotiation, but there may be some cases where you need to specify the content type explicitly.

Step 6 -> Adding function handlers to routes.(companies/urls.py)

Whenever user visit the user, a function is called in view which takes care of response.

stock_rest_api/urls.py

from django.conf.urls import url, include
from django.contrib import admin
from rest_framework.urlpatterns import format_suffix_patterns
from companies import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^companies/', include('companies.urls')),
]

                        

Adding sub path for companies app(companies/urls.py)

It defines the particular path for companies app

from django.urls import path

from . import views

app_name = 'companies'
urlpatterns = [
    # IT IS FOR JSON RESPONSE SERIALIZER
    # path('', views.stock, name='list'),
    # path('', views.StockApiView.as_view(), name='list'),
    path('', views.GenericStockView.as_view(), name='list'),
    path('<int:pk>/', views.StockApiViewDetail.as_view(), name='detail'),
    # path('<int:pk>/', views.stock_details, name='detail'),
    path('login/', views.LoginApiView.as_view(), name="login"),
    path('logout/', views.LogoutApiView.as_view(), name="logout")
]
                        

Add Some styles to make it attractive

Deployment

You can easily deploy on Heroku

You can read more about on Analytics Vidhya Blog

Web Preview / Output

web preview Web preview on deployment

Placeholder text by Praveen Chaudhary · Images by Binary Beast