Skip to content

Deployed on https://onedsassignment.herokuapp.com/. The Project is based upon Django Backend and React frontend. User Authentication using django-rest-knox.

Notifications You must be signed in to change notification settings

VaibhavSaini2000/heroku_django_react_webpack

Repository files navigation

Restful-api

Data Dashboard with Django-Rest framework

This is a Dashboard app built using Django REST Framework for backend and ReactJS for frontend. It include 4 charts with filters for year, sales and profit.

It has the following features:

  • The backend is hosted at heroku, using PostgreSQL for database
  • ReactJS Frontend consumes data prepared by Rest-API in JSON format
  • User Authentication with session tokens
  • Session management including time spent on website and interaction with charts/filters

Requirements

  • Python 3.6 or higher
  • Django 3.0 or higher
  • djangorestframework (3.8+)
  • ReactJS

Usage

The API calls are generated using serializer with following structure:

from rest_framework import serializers

class SegSerializer(serializers.Serializer):
    segment = serializers.CharField(max_length=25, required=True)
    year = serializers.IntegerField()
    sales = serializers.DecimalField(max_digits=12, decimal_places=5, required=True)
    profit = serializers.DecimalField(max_digits=12, decimal_places=5, required=True)

    class Meta:
        fields = ('segment', 'year', 'sales', 'profit')

The views.py file generates required object query and calls on serializers to generate JSON response for the API. This serializer will automatically convert all data fields obtained using filters applied to model objects and we receive a complete instance with filled data.

from rest_framework.generics import ListAPIView
from rest_framework.response import Response
from restapi.models import superstore
from .serializers import SegSerializer

class segmentList(ListAPIView):
    queryset = ''
    def list(self, request):
        query1 = superstore.objects.annotate(year=ExtractYear('order_date')).values('segment', 'year')\
            .annotate(sales=Sum('sales'), profit=Sum('profit')).order_by('year')
        serializer =SegSerializer(list(query1), many=True)

        return Response(serializer)

The APIList view can be obtained from the link specified at urls.py file. For example, we get the following data with related nested fields from our 'type' serializer:

{
    "segment": {
        "2014": [
            {
                "segment": "Consumer",
                "year": 2014,
                "sales": "262956.80060",
                "profit": "23837.31970"
            },
            {
                "segment": "Corporate",
                "year": 2014,
                "sales": "127797.49570",
                "profit": "13325.91250"
            },
            {
                "segment": "Home Office",
                "year": 2014,
                "sales": "89101.91180",
                "profit": "11724.99990"
            }
        ],
        "2015": [...]
        ...
        }
}

ReactJS Based Frontend

Session Management

User Authentication

https://www.valentinog.com/blog/drf/ and https://www.youtube.com/playlist?list=PLillGF-RfqbbRA-CIUxlxkUpbq0IFkX60

Here, we have used the User model which comes default with Django.

from django.contrib.auth.models import User

We have used the django-rest-knox library. Django-rest-knox library provides models and views to handle token based authentication in a more secure and extensible way than the built-in TokenAuthentication scheme - with Single Page Applications and Mobile clients in mind. It provides per-client tokens, and views to generate them when provided some other authentication (usually basic authentication), to delete the token (providing a server enforced logout) and to delete all tokens (logs out all clients that a user is logged into).

<!--Inside onedsdjangoproject/settings.py-->
INSTALLED_APPS = [
    ...,
    'rest_framework',
    'knox',
    'accounts'
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ('knox.auth.TokenAuthentication',)
}

So, after making migrations the registered Users' data gets stored in auth_user table.

We have created a different Django 'app' called "accounts" for handling authentication.

python manage.py startapp accounts

Creating and Validating the User:-

<!-- Inside accounts/serializers.py-->
from rest_framework import serializers
from django.contrib.auth.models import User
from django.contrib.auth import authenticate

class UserSerializer(serializers.ModelSerializer):
  class Meta:
    model = User
    fields = ('id', 'username', 'email')

class RegisterSerializer(serializers.ModelSerializer):
  class Meta:
    model = User
    fields = ('id', 'username', 'email', 'password')
    extra_kwargs = {'password': {'write_only': True}}

  def create(self, validated_data):
    user = User.objects.create_user(validated_data['username'], validated_data['email'], validated_data['password'])

    return user

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

  def validate(self, data):
    user = authenticate(**data)
    if user and user.is_active:
      return user
    raise serializers.ValidationError("Incorrect Credentials")

Authentication related Post requests coming from frontend are handled as below:-

<!--Inside accounts/api.py-->

from rest_framework import generics, permissions
from rest_framework.response import Response
from knox.models import AuthToken
from .serializers import UserSerializer, RegisterSerializer, LoginSerializer

class RegisterAPI(generics.GenericAPIView):
  serializer_class = RegisterSerializer

  def post(self, request, *args, **kwargs):
    serializer = self.get_serializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    user = serializer.save()
    return Response({
      "user": UserSerializer(user, context=self.get_serializer_context()).data,
      "token": AuthToken.objects.create(user)[1]
    })

class LoginAPI(generics.GenericAPIView):
  serializer_class = LoginSerializer

  def post(self, request, *args, **kwargs):
    serializer = self.get_serializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    user = serializer.validated_data
    _, token = AuthToken.objects.create(user)
    return Response({
      "user": UserSerializer(user, context=self.get_serializer_context()).data,
      "token": token
    })

class UserAPI(generics.RetrieveAPIView):
  permission_classes = [
    permissions.IsAuthenticated,
  ]
  serializer_class = UserSerializer

  def get_object(self):
    return self.request.user

Axios API requests from frontend are made to the following URLs for authentication related tasks.

<!--Inside accounts/urls.py-->
from django.urls import path, include
from .api import RegisterAPI, LoginAPI, UserAPI
from knox import views as knox_views

urlpatterns = [
  path('api/auth', include('knox.urls')),
  path('api/auth/register', RegisterAPI.as_view()),
  path('api/auth/login', LoginAPI.as_view()),
  path('api/auth/user', UserAPI.as_view()),
  path('api/auth/logout', knox_views.LogoutView.as_view(), name='knox_logout')
]

Frontend API calls via axios are made as below:-

<!--Inside frontend/src/actions/auth.js-->
import axios from 'axios';
import { returnErrors } from './messages';

import {
  USER_LOADED,
  USER_LOADING,
  AUTH_ERROR,
  LOGIN_SUCCESS,
  LOGIN_FAIL,
  LOGOUT_SUCCESS,
  REGISTER_SUCCESS,
  REGISTER_FAIL,
} from './types';

// CHECK TOKEN & LOAD USER
export const loadUser = () => (dispatch, getState) => {
  // User Loading
  dispatch({ type: USER_LOADING });

  axios
    .get('/api/auth/user', tokenConfig(getState))
    .then((res) => {
      dispatch({
        type: USER_LOADED,
        payload: res.data,
      });
    })
    .catch((err) => {
      dispatch(returnErrors(err.response.data, err.response.status));
      dispatch({
        type: AUTH_ERROR,
      });
    });
};

// LOGIN USER
export const login = (username, password) => (dispatch) => {
  // Headers
  const config = {
    headers: {
      'Content-Type': 'application/json',
    },
  };

  // Request Body
  const body = JSON.stringify({ username, password });

  axios
    .post('/api/auth/login', body, config)
    .then((res) => {
      dispatch({
        type: LOGIN_SUCCESS,
        payload: res.data,
      });
    })
    .catch((err) => {
      dispatch(returnErrors(err.response.data, err.response.status));
      dispatch({
        type: LOGIN_FAIL,
      });
    });
};

// REGISTER USER
export const register = ({ username, password, email }) => (dispatch) => {
  // Headers
  const config = {
    headers: {
      'Content-Type': 'application/json',
    },
  };

  // Request Body
  const body = JSON.stringify({ username, email, password });

  axios
    .post('/api/auth/register', body, config)
    .then((res) => {
      dispatch({
        type: REGISTER_SUCCESS,
        payload: res.data,
      });
    })
    .catch((err) => {
      dispatch(returnErrors(err.response.data, err.response.status));
      dispatch({
        type: REGISTER_FAIL,
      });
    });
};

// LOGOUT USER
export const logout = () => (dispatch, getState) => {
  axios
    .post('/api/auth/logout/', null, tokenConfig(getState))
    .then((res) => {
      dispatch({
        type: LOGOUT_SUCCESS,
      });
    })
    .catch((err) => {
      dispatch(returnErrors(err.response.data, err.response.status));
    });
};

// Setup config with token - helper function
export const tokenConfig = (getState) => {
  // Get token from state
  const token = getState().auth.token;

  // Headers
  const config = {
    headers: {
      'Content-Type': 'application/json',
    },
  };

  // If token, add to headers config
  if (token) {
    config.headers['Authorization'] = `Token ${token}`;
  }

  return config;
};

Also, inside onedsdashboardapp/views.py file we have used the following

permission_classes = [
        permissions.AllowAny,
    ]

but we can use

permission_classes = [
        permissions.IsAuthenticated,
    ]

if we don't want the charts' data to be rendered for unauthorized users.

Authors

  • github/ppundeer
  • github/VaibhavSaini2000
  • github/hitesh0902

About

Deployed on https://onedsassignment.herokuapp.com/. The Project is based upon Django Backend and React frontend. User Authentication using django-rest-knox.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published