Personal Finance Tracker using Django
In this tutorial, we’ll develop a Personal Finance Tracker using Django. We’ll implement a robust login and logout system, and then proceed to establish CRUD (Create, Read, Update, Delete) operations for efficiently managing our expenses. The system will empower users to seamlessly delete or update their data with a single click, avoiding the need to navigate to the admin panel for such tasks. Once all necessary data is entered, a simple button click will trigger the automatic generation of a comprehensive financial overview, calculated based on the entered expenses.
What is a Finance Tracker?
A Finance Tracker is a Django-based web application that helps users manage personal finances. It includes a secure login, and CRUD operations for expense management, and allows easy one-click data deletion and updates. Users can generate a comprehensive financial overview by simply clicking a button after entering their expenses. which we will create using Python Django.
Personal Finance Tracker using Django
To install Django follow these steps.
Starting the Project Folder
To start the project, and app use this command
django-admin startproject core
cd core
python manage.py startapp home
Now add this app to the ‘settings.py’
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"home",
]
File Structure
Setting Necessary Files
models.py : Below code defines a Django model “Expense” with fields for user, salary, name, and price, tailored for managing expense data, associating each expense with a user and default values for salary, name, and price.
Python3
from django.db import models from django.contrib.auth.models import User #Create mode for Expense class Expense(models.Model): user = models.ForeignKey(User, on_delete = models.SET_NULL, null = True , blank = True ) salary = models.IntegerField(default = 0 ) name = models.CharField(max_length = 100 , default = 'something' ) price = models.IntegerField(default = 0 ) |
views.py : Below code is a Django web application implementing expense management functionality. It includes features for creating, updating, and deleting expenses, as well as user authentication (login, registration, and logout). The application also generates a bill (PDF) displaying user-specific expenses and total sum. User authentication is enforced for certain views, and the data is stored in the database using the Django Expense model. The application is organized with views for rendering HTML templates, and it utilizes Django’s authentication system for user handling.
Python3
# Import necessary libraries from django.shortcuts import render, redirect from django.contrib.auth.decorators import login_required from django.contrib.auth import authenticate, login, logout from django.contrib import messages from django.contrib.auth.models import User # Import User model from .models import Expense # Create Expense page @login_required (login_url = '/login/' ) def expenses(request): salary = 0 if request.method = = 'POST' : data = request.POST salary = int (data.get( 'salary' , 0 )) name = data.get( 'name' ) price = int (data.get( 'price' , 0 )) Expense.objects.create( salary = salary, name = name, price = price, ) return redirect( '/' ) queryset = Expense.objects. all () if request.GET.get( 'search' ): queryset = queryset. filter ( name__icontains = request.GET.get( 'search' )) # Calculate the total sum total_sum = sum (expense.price for expense in queryset) context = { 'expenses' : queryset, 'total_sum' : total_sum} return render(request, 'expenses.html' , context) # Update the Expenses data @login_required (login_url = '/login/' ) def update_expense(request, id ): queryset = Expense.objects.get( id = id ) if request.method = = 'POST' : data = request.POST name = data.get( 'name' ) price = int (data.get( 'price' , 0 )) queryset.name = name queryset.price = price queryset.save() return redirect( '/' ) context = { 'expense' : queryset} return render(request, 'update_expense.html' , context) # Delete the Expenses data @login_required (login_url = '/login/' ) def delete_expense(request, id ): queryset = Expense.objects.get( id = id ) queryset.delete() return redirect( '/' ) # Login page for user def login_page(request): if request.method = = "POST" : try : username = request.POST.get( 'username' ) password = request.POST.get( 'password' ) user_obj = User.objects. filter (username = username).first() if not user_obj: messages.error(request, "Username not found" ) return redirect( '/login/' ) user_auth = authenticate(username = username, password = password) if user_auth: login(request, user_auth) return redirect( 'expenses' ) messages.error(request, "Wrong Password" ) return redirect( '/login/' ) except Exception as e: messages.error(request, "Something went wrong" ) return redirect( '/register/' ) return render(request, "login.html" ) # Register page for user def register_page(request): if request.method = = "POST" : try : username = request.POST.get( 'username' ) password = request.POST.get( 'password' ) user_obj = User.objects. filter (username = username) if user_obj.exists(): messages.error(request, "Username is taken" ) return redirect( '/register/' ) user_obj = User.objects.create(username = username) user_obj.set_password(password) user_obj.save() messages.success(request, "Account created" ) return redirect( '/login' ) except Exception as e: messages.error(request, "Something went wrong" ) return redirect( '/register' ) return render(request, "register.html" ) # Logout function def custom_logout(request): logout(request) return redirect( 'login' ) # Generate the Bill @login_required (login_url = '/login/' ) def pdf(request): if request.method = = 'POST' : data = request.POST salary = int (data.get( 'salary' )) name = data.get( 'name' ) price = int (data.get( 'price' , 0 )) Expense.objects.create( salary = salary, name = name, price = price, ) return redirect( 'pdf' ) queryset = Expense.objects. all () if request.GET.get( 'search' ): queryset = queryset. filter ( name__icontains = request.GET.get( 'search' )) # Calculate the total sum total_sum = sum (expense.price for expense in queryset) # Get the username username = request.user.username context = { 'expenses' : queryset, 'total_sum' : total_sum, 'username' :username} return render(request, 'pdf.html' , context) |
urls.py : Below code sets up URL patterns for a Django web app, linking specific URLs to corresponding views in the ‘home’ app. It includes paths for logging out, generating PDFs, accessing the admin site, logging in, registering, managing expenses, updating expenses, and deleting expenses. Each URL is associated with a specific view function.
Python3
from django.contrib import admin from django.urls import path from home import views urlpatterns = [ path( 'logout/' , views.custom_logout, name = "logout" ), path( 'pdf/' , views.pdf , name = 'pdf' ), path( 'admin/' , admin.site.urls), path( 'login/' , views.login_page, name = 'login' ), path( 'register/' , views.register_page, name = 'register' ), path(' ', views.expenses, name=' expenses'), path( 'update_expense/<id>' , views.update_expense, name = 'update_expense' ), path( 'delete_expense/<id>' , views.delete_expense, name = 'delete_expense' ), ] |
Creating GUI
base.html : Below, HTML template includes a basic structure with a head section defining metadata, a title, and some inline CSS styling. The body section contains a table with specific styling for rows and cells. The template includes a block named “start” which can be extended or overridden in child templates. Additionally, a simple JavaScript script is included in the body.
HTML
<!DOCTYPE html> < html lang = "en" > < head > < meta charset = "UTF-8" > < meta name = "viewport" content = "width=device-width, initial-scale=1.0" > < title >{{page}}</ title > < style > table { width: 80%; margin: 20px auto; border-collapse: collapse; } th, td { padding: 10px; text-align: left; border: 1px solid #ccc; } th { background-color: #f2f2f2; } tr:nth-child(even) { background-color: #f2f2f2; } tr:hover { background-color: #ddd; } </ style > </ head > < body > {% block start %} {% endblock %} < script > console.log('Hey Django') </ script > </ body > </ html > |
expeses.html : This Django template extends a base template, adding a form for entering expenses with fields for reason and amount. It includes buttons to submit the form, view total expenses, and logout. A table displays a list of expenses with options to delete or update each entry. The template incorporates Bootstrap and custom styles for visual appeal.
HTML
{% extends "base.html" %} {% block start %} < link href = "https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel = "stylesheet" > < style > .text { color: green; font-weight: bold; font-family: 'Times New Roman', Times, serif; } .ok { color: white; text-decoration: none; } .ok:hover { color: white; text-decoration: none; } </ style > < div class = "container mt-5 col-6" > < form class = "col-6 mx-auto card p-3 shadow-lg" method = "post" enctype = "multipart/form-data" > {% csrf_token %} < br > < h4 style = "font-family: 'Times New Roman', Times, serif;" > Enter Your Expenses</ h4 > < hr > < div class = "form-group" > < label for = "exampleInputEmail1" >Expenses Reason </ label > < input type = "text" name = "name" class = "form-control" required> </ div > < div class = "form-group" > < label for = "exampleInputEmail1" >Amount</ label > < input name = "price" type = "number" class = "form-control" required> </ div > < button type = "submit" class = "btn btn-success" >Add Data</ button > </ form > < div class = "class mt-5" > < form action = "" > < button class = "btn btn-primary" > < a class = "ok" href = "{% url 'pdf' %}" >Total Expenses </ a ></ button > < button class = "btn btn-danger" > < a class = "ok" href = "{% url 'logout' %}" >Logout </ a ></ button > </ form > < table class = "table mt-6" > < thead > < tr > < th scope = "col" >S.No. </ th > < th scope = "col" >Expenses Reason</ th > < th scope = "col" >Amount </ th > < th scope = "col" >Actions</ th > </ tr > </ thead > < tbody > {% for expense in expenses %} < tr > < th scope = "row" >{{forloop.counter}}</ th > < td >{{expense.name}}</ td > < td > ₹{{expense.price}}</ td > < td > < a href = "/delete_expense/{{expense.id }}" class = "btn btn-danger m-2" >Delete </ a > < a href = "/update_expense/{{expense.id }}" class = "btn btn-success" >Update </ a > </ td > </ tr > {% endfor %} </ tbody > </ table > </ div > {% endblock %} |
login.html : This HTML template is designed for a login page in a job portal. It uses Bootstrap 5 for styling and includes a form for users to enter their username and password. The form has a login button, and there is a link to create a new account. Success messages, if any, are displayed in an alert. The template provides a clean and simple user interface for logging into the job portal.
HTML
<!doctype html> < html lang = "en" > < head > <!-- Required meta tags --> < meta charset = "utf-8" > < meta name = "viewport" content = "width=device-width, initial-scale=1" > < link href = "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel = "stylesheet" integrity = "sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin = "anonymous" > < link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" > < title >Job Portal</ title > </ head > < style > </ style > < body >< br >< br >< br >< br > < br >< br > < div class = "container mt-4 bg-white col-md-3 card shadow p-3 " id = "log" > < div class = "login-form" > {% if messages %} {% for message in messages %} < div class = "alert alert-success {{ message.tags }} mt-4" role = "alert" > {{ message }} </ div > {% endfor %} {% endif %} < form action = "" method = "post" > {% csrf_token %} < h4 class = "text-center" style = "font-family: 'Times New Roman', Times, serif;" > Login </ h4 > < div class = "form-group" > < input type = "text" class = "form-control" name = "username" placeholder = "Username" required style = "background-color: #fff; border: 1px solid #ddd; border-radius: 5px; padding: 10px;" > </ div > < div class = "form-group mt-2" > < input type = "password" class = "form-control" name = "password" placeholder = "Password" required style = "background-color: #fff; border: 1px solid #ddd; border-radius: 5px; padding: 10px;" > </ div > < div class = "form-group mt-2" > < button class = "btn btn-success btn-block" style = "margin-left: 138px;" >Login</ button > </ div > < br > </ form > < p class = "text-center" style = "color: #555;" >< a href = "{% url 'register' %}" style = "color: #007bff;" >Create an Account</ a ></ p > </ div > </ div > </ body > </ html > |
register.html : Below, HTML template is for a registration page in a job portal, using Bootstrap 5 for styling. It includes a form for users to register with a username and password. The form has a registration button, and there is a link to the login page. Success messages, if any, are displayed in an alert. The template provides a clean and straightforward user interface for user registration.
HTML
<!doctype html> < html lang = "en" > < head > <!-- Required meta tags --> < meta charset = "utf-8" > < meta name = "viewport" content = "width=device-width, initial-scale=1" > < link href = "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel = "stylesheet" integrity = "sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin = "anonymous" > < link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" > < title >Job Portal</ title > </ head > < body > < body > < br > < br >< br >< br >< br >< br > < div class = "container mt-4 bg-white mx-auto col-md-3 card shadow p-3" > < div class = "login-form" > {% if messages %} {% for message in messages %} < div class = "alert alert-success {{ message.tags }}" role = "alert" > {{ message }} </ div > {% endfor %} {% endif %} < form action = "" method = "post" > {% csrf_token %} < h4 class = "text-center" style = "font-family: 'Times New Roman', Times, serif;" > Register </ h4 > < div class = "form-group" > < input type = "text" class = "form-control" name = "username" placeholder = "Username" required> </ div > < div class = "form-group mt-2" > < input type = "password" class = "form-control" name = "password" placeholder = "Password" required> </ div > < div class = "form-group mt-2" > < button class = "btn btn-success btn-block" style = "margin-left: 117px;" >Register</ button > </ div > < br > </ form > < p class = "text-center" >< a href = "{% url 'login' %}" >Log In</ a ></ p > </ div > </ div > </ body > </ html > |
update_expenses.html : This Django template extends a “base.html” template and overrides the “start” block. It features a form for updating expense data with pre-filled fields for expense reason and amount. The styling utilizes Bootstrap, creating a clean and straightforward user interface for expense updates.
HTML
{% extends "base.html" %} {% block start %} < link href = "https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel = "stylesheet" > < style > .text { color: green; font-weight: bold; font-family: 'Times New Roman', Times, serif; } </ style > < div class = "container mt-5 col-5" > < form class = "col-6 mx-auto card p-3 shadow-lg" method = "post" enctype = "multipart/form-data" > {% csrf_token %} < h4 style = "font-family: 'Times New Roman', Times, serif; font-size:20px;" >Update Data</ h4 > < hr > < div class = "form-group" > < label for = "exampleInputEmail1" >Expenses Reason</ label > < input type = "text" name = "name" value = "{{expense.name}}" class = "form-control" required> </ div > < div class = "form-group" > < label for = "exampleInputEmail1" >Amount</ label > < input name = "price" type = "number" value = "{{expense.price}}" class = "form-control" required> </ div > < button type = "submit" class = "btn btn-danger" >Update </ button > </ form > </ div > {% endblock %} |
pdf.html : In below code, Bootstrap-styled HTML template displays a user’s expense report. It includes a header with a finance record and an image, a table showing expenses, a total expenses calculation, and a personalized footer message. The template offers a clean and organized design with a print button for user convenience.
HTML
<!DOCTYPE html> < html lang = "en" > < head > < meta charset = "UTF-8" > < meta name = "viewport" content = "width=device-width, initial-scale=1.0" > < title >Expense</ title > <!-- Add Bootstrap CSS Link --> < link href = "https://cdn.jsdelivr.net/npm/bootstrap@4.5.2/dist/css/bootstrap.min.css" rel = "stylesheet" > < style > body { font-family: 'Times New Roman', Times, serif; } .container { max-width: 700px; margin: 30px auto; } .expense-container { padding: 20px; border: 1px solid #000; border-radius: 5px; background-color: #fff; margin-top: 30px; } .expense-header { text-align: center; } .expense-title { font-size: 24px; color: #333; margin-top: 20px; } .expense-table { width: 100%; margin-top: 20px; border-collapse: collapse; } .expense-table th, .expense-table td { border: 1px solid #ddd; padding: 12px; text-align: left; } .expense-total { font-size: 20px; font-weight: bold; color: #333; margin-top: 20px; } .expense-footer { margin-top: 20px; font-size: 16px; color: #555; } .btn-print { margin-left: 48%; margin-top: 2%; } img { width: 140px; height: 150px; } #date { margin-left: 120px; } </ style > </ head > < body > < div class = "container expense-container" > < div class = "expense-header" > < img src = "https://i.ibb.co/7QJjfxV/images-1.jpg" alt = "" > < div class = "expense-title" > < h4 >{{ username }}, Your Finance Record</ h4 > </ div > </ div > < br > < table class = "expense-table" > < thead > < tr > < th >Expenses Reason</ th > < th >Amount</ th > </ tr > </ thead > < tbody > {% for expense in expenses %} < tr > < td >{{ expense.name }}</ td > < td > ₹{{ expense.price }}</ td > </ tr > {% endfor %} </ tbody > </ table > < p class = "expense-total" >Total Expenses: ₹{{ total_sum }}</ p > < div class = "expense-footer" > < p >Dear {{ username }}, You Expenses is ₹{{total_sum}}.</ p > </ div > </ div > <!-- Add Bootstrap JS and jQuery --> < script src = "https://code.jquery.com/jquery-3.5.1.slim.min.js" ></ script > < script src = "https://cdn.jsdelivr.net/npm/bootstrap@4.5.2/dist/js/bootstrap.min.js" ></ script > </ body > </ html > |
admin.py : Here we are registering our models.
Python3
from django.contrib import admin from .models import * from django.db.models import Sum admin.site.register(Expense) |
Deployement of the Project
Run these commands to apply the migrations:
python3 manage.py makemigrations
python3 manage.py migrate
Run the server with the help of following command:
python3 manage.py runserver
Output :