Building a Reusable Core Package for Your Django REST Framework Expense Tracker — Part 4
If you enjoyed this, buy me a coffee! ☕💖
Core Package Structure
Let’s start by setting up the core structure for our Expense Tracker package. This package will include essential components such as paginators, API views, constants, exceptions, and serializers, setting the foundation for a robust and scalable application.
core/
├── filters/
│ ├── category_filter.py
│ └── expense_filter.py
├── serializers/
│ ├── balance_serializer.py
│ ├── category_serializer.py
│ ├── expense_serializer.py
│ └── user_serializer.py
├── views/
│ ├── balance_views.py
│ ├── category_views.py
│ ├── expense_views.py
│ ├── export_expense_views.py
│ ├── raw_query_views.py
│ └── user_views.py
├── apis_views.py
├── cons.py
├── exception.py
├── paginators.py
└── urls.py
Constants (cons.py)
# cons.py
DATA = 'data'
ERRORS = 'errors'
MESSAGE = 'message'
Exceptions (exception.py)
# exception.py
class InvalidData(Exception):
def __init__(self, message):
self.message = message
def get_message(self):
return self.message
Paginators (paginators.py)
# paginators.py
from collections import OrderedDict
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
class ExpenseTrackerPaginator(PageNumberPagination):
template = None
page_query_param = 'page'
page_size_query_param = 'page_size'
def get_paginated_response(self, data):
return Response(OrderedDict({
'data': data,
'pagination': {
'count': self.page.paginator.count,
'next': self.get_next_link(),
'previous': self.get_previous_link()
}
}))
API Views (apis_views.py)
import json
from django.core.exceptions import ObjectDoesNotExist
from django.db import IntegrityError
from django.db.transaction import atomic
from django.http import Http404
from rest_framework import status
from rest_framework.exceptions import ValidationError
from rest_framework.generics import CreateAPIView, ListAPIView, RetrieveUpdateDestroyAPIView
from rest_framework.response import Response
from core.cons import DATA, ERRORS, MESSAGE
from core.exception import InvalidData
class BaseExpenseTrackerAPIView:
queryset = None
serializer_class = None
read_serializer_class = None
write_serializer_class = None
filter_serializer_class = None
filter_map = {}
def get_serializer_class(self):
if self.request.method in ['GET', 'HEAD']:
return self.read_serializer_class or self.serializer_class
elif self.request.method in ['POST', 'PUT', 'PATCH']:
return self.write_serializer_class or self.serializer_class
return self.serializer_class
@staticmethod
def handle_exception(exception):
response_data = {
ERRORS: str(exception),
MESSAGE: 'Server Error'
}
if isinstance(exception, ValidationError):
response_data.update({
ERRORS: exception.get_full_details()
})
response_status = status.HTTP_400_BAD_REQUEST
elif isinstance(exception, ObjectDoesNotExist):
response_data.update({
MESSAGE: 'Object does not exist',
})
response_status = status.HTTP_404_NOT_FOUND
elif isinstance(exception, IntegrityError):
response_data.update({
MESSAGE: 'Error in database',
})
response_status = status.HTTP_500_INTERNAL_SERVER_ERROR
elif isinstance(exception, InvalidData):
response_data.update({
MESSAGE: exception.get_message(),
})
response_status = status.HTTP_400_BAD_REQUEST
else:
response_status = status.HTTP_500_INTERNAL_SERVER_ERROR
return response_data, response_status
class ExpenseTrackerListAPIView(BaseExpenseTrackerAPIView, ListAPIView):
pass
class ExpenseTrackerCreateAPIView(BaseExpenseTrackerAPIView, CreateAPIView):
pass
class ExpenseTrackerListCreateAPIView(ExpenseTrackerListAPIView, ExpenseTrackerCreateAPIView):
pass
class ExpenseTrackerRetrieveUpdateDestroyAPIView(BaseExpenseTrackerAPIView, RetrieveUpdateDestroyAPIView):
pass
Conclusion
By building a reusable core package, we have laid the groundwork for a scalable and secure Expense Tracker application. This modular approach ensures that we can easily extend and maintain the application as it grows. Stay tuned for the next part of this series, where we will dive deeper into the Expense Tracker features and functionalities.
If you found this article helpful, don’t forget to share it with your friends and colleagues! 📤 Feel free to leave a comment below with your thoughts or questions 💬, and give it a like if you enjoyed it! 👍😊
If you enjoyed this, consider buying me a Coffee! ☕💖