I want to add filters and sorters like filter by amount, category and date. Sort by name, amount, category and date, both ascending and descending. Also, I want to be able to apply more than 1 filters. And sin sorting, if I first chose amount, then it’s first sorted by amount, then if I chose date, then entries with the same amount should be sorted by date. Also, can I do it dynamically or do I have to refresh the page everytime a filter is applied or removed. How should I go about this?
views.py:-
from django.shortcuts import render, redirect
from django.urls import reverse_lazy
from django.views.generic import View, UpdateView, DeleteView
from .forms import ExpenseForm
from .models import Expense
from django.db.models import Sum
import datetime
# Create your views here.
class IndexView(View):
template_name = 'expenses/index.html'
def get(self, request):
expense_form = ExpenseForm()
expenses = Expense.objects.all().order_by('-date')
total_expenses = Expense.objects.aggregate(Sum('amount'))
today = datetime.date.today()
# last year sum
last_year = today - datetime.timedelta(days=365)
yearly_sum = Expense.objects.filter(date__gt=last_year, user=request.user).aggregate(Sum('amount'))
# last month sum
last_month = today - datetime.timedelta(days=30)
monthly_sum = Expense.objects.filter(date__gt=last_month, user=request.user).aggregate(Sum('amount'))
# last week sum
last_week = today - datetime.timedelta(days=7)
weekly_sum = Expense.objects.filter(date__gt=last_week, user=request.user).aggregate(Sum('amount'))
year_start = datetime.date(today.year, 1, 1)
month_start = datetime.date(today.year, today.month, 1)
week_start = today - datetime.timedelta(days=today.weekday())
# sums from since starting of...
this_year_sum = Expense.objects.filter(date__gt=year_start, user=request.user).aggregate(Sum('amount'))
this_month_sum = Expense.objects.filter(date__gt=month_start, user=request.user).aggregate(Sum('amount'))
this_week_sum = Expense.objects.filter(date__gt=week_start, user=request.user).aggregate(Sum('amount'))
return render(request, self.template_name, {
'expense_form': expense_form,
'expenses': expenses,
'total_expenses': total_expenses,
'yearly_sum': yearly_sum,
'monthly_sum': monthly_sum,
'weekly_sum': weekly_sum,
'this_year_sum': this_year_sum,
'this_month_sum': this_month_sum,
'this_week_sum': this_week_sum,
})
def post(self, request):
expense_form = ExpenseForm(request.POST)
if expense_form.is_valid():
expense = expense_form.save(commit=False)
expense.user = request.user
expense.save()
return redirect('index')
expenses = Expense.objects.all()
return render(request, self.template_name, {'expense_form': expense_form, 'expenses': expenses})
class ExpenseUpdateView(UpdateView):
model = Expense
fields = ['name', 'amount', 'category']
template_name_suffix = '_update_form'
success_url = reverse_lazy('index')
class ExpenseDeleteView(DeleteView):
model = Expense
success_url = reverse_lazy('index')
models.py:-
from django.db import models
from django.contrib.auth.models import User
from datetime import date
from django.db.models import Sum
# Create your models here.
class Expense(models.Model):
name = models.CharField(max_length=200)
amount = models.IntegerField()
category = models.CharField(max_length=200)
date = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='expenses')
def __str__(self):
return self.name
index.html:-
{% extends "base.html" %}
{% load humanize %}
{% block content %}
<h1 class="text-4xl mt-10 text-center">Best Place To Keep Track Of Your Money! 💰</h1>
<h2 class="font-bold text-gray-700 mt-10 px-10 -mb-10">Add Expense ➕</h2>
<form class="shadow-lg m-10 rounded-lg" method="post">
{% csrf_token %}
<div class="form-container pt-4 pb-8 px-10 flex items-center justify-center gap-10">
{% for field in expense_form %}
<div class="relative">
<div>
<div class="mb-1">
<label class="text-lg text-gray-500 font-bold" for="id_{{field.name}}">{{field.name | title}}</label>
</div>
<div class="border-2 border-zinc-400 focus:outline-none xl:w-80">
{{field}}
</div>
</div>
<div class="absolute top-16 px-1 py-1 text-sm font-semibold text-red-600">
{{field.errors}}
</div>
</div>
{% endfor %}
<div class="pt-8">
<button class="bg-green-500 hover:bg-green-600 px-5 py-2 rounded text-white font-bold">Add</button>
</div>
</div>
</form>
<div class="container px-10 my-5 rounded-lg">
<h1 class="text-4xl my-10 text-center">💵 Expenses 💵</h1>
<div class="shadow-lg p-5">
{% if expenses %}
<div class="expense-header text-xl flex flex-wrap px-20 font-bold">
<span>Name</span>
<span>Amount</span>
<span>Category</span>
<span>Date</span>
<span>Edit</span>
<span>Delete</span>
</div>
<hr class="mt-8">
{% else %}
<h1 class="text-4xl text-center">Add Expenses to show here...</h1>
{% endif %}
{% for expense in expenses %}
<div class="expense-row flex flex-wrap px-20 py-5">
<span>{{expense.name}}</span>
<span>₹ {{expense.amount | intcomma}}</span>
<span>{{expense.category}}</span>
<span>{{expense.date | date:"d M, Y"}}</span>
<span>
<div class="flex justify-center">
<a class="flex justify-content p-1" href="{% url 'edit' expense.id %}">
<svg class="stroke-indigo-800 stroke-2 w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
</svg>
</a>
</div>
</span>
<span>
<form method="POST" action="{% url 'delete' expense.id %}">
{% csrf_token %}
<button class="cursor-pointer p-1" type="submit">
<svg class="stroke-red-600 stroke-2 w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
</svg>
</button>
</form>
</span>
</div>
{% endfor %}
<div class="px-60 mt-10">
<span class="mx-2 font-bold">Total: </span>
<span class="font-bold text-green-600 text-2xl">₹ {{total_expenses.amount__sum | intcomma}}</span>
</div>
</div>
<h1 class="text-4xl mt-20 mb-5 text-center">📜 Totals 🧾</h1>
<div class="container">
<div class="flex justify-center">
<div class="shadow-lg rounded-lg p-10 m-10 w-1/3">
<h2 class="font-bold text-gray-500">Last Year (365 days)</h2>
<h2 class="font-bold text-green-600 text-2xl">
₹ {{yearly_sum.amount__sum | intcomma}}
</h2>
</div>
<div class="shadow-lg rounded-lg p-10 m-10 w-1/3">
<h2 class="font-bold text-gray-500">Last Month (30 days)</h2>
<h2 class="font-bold text-green-600 text-2xl">
₹ {{weekly_sum.amount__sum | intcomma}}
</h2>
</div>
<div class="shadow-lg rounded-lg p-10 m-10 w-1/3">
<h2 class="font-bold text-gray-500">Last Week (7 days)</h2>
<h2 class="font-bold text-green-600 text-2xl">
₹ {{monthly_sum.amount__sum | intcomma}}
</h2>
</div>
</div>
</div>
<div class="container">
<div class="flex justify-center">
<div class="shadow-lg rounded-lg p-10 m-10 w-1/3">
<h2 class="font-bold text-gray-500">This Year</h2>
<h2 class="font-bold text-green-600 text-2xl">
₹ {{this_year_sum.amount__sum | intcomma}}
</h2>
</div>
<div class="shadow-lg rounded-lg p-10 m-10 w-1/3">
<h2 class="font-bold text-gray-500">This Month</h2>
<h2 class="font-bold text-green-600 text-2xl">
₹ {{this_week_sum.amount__sum | intcomma}}
</h2>
</div>
<div class="shadow-lg rounded-lg p-10 m-10 w-1/3">
<h2 class="font-bold text-gray-500">This Week</h2>
<h2 class="font-bold text-green-600 text-2xl">
₹ {{this_month_sum.amount__sum | intcomma}}
</h2>
</div>
</div>
</div>
</div>
{% endblock content %}