Django-powered personal finance tracker with income/expense management, visual statistics, and secure user authentication. Perfect for personal budgeting and financial analysis.
A clean, production-ready personal finance manager with user authentication, transaction tracking, budget limits with email alerts, CSV import/export, and a full REST API with Swagger documentation. Built as a portfolio project demonstrating Django best practices, Docker infrastructure, and REST API design.
- Create, edit, and delete income/expense transactions
- Categorize transactions with user-specific categories
- Filter by type, category, and date range
- Pagination (10 transactions per page)
- Export transactions to CSV
- Import transactions from CSV with validation and error reporting
- Set budget limits per category (daily / weekly / monthly)
- Real-time budget alerts with progress bars
- Email notifications when budget is exceeded
- Visual warnings at 80%+ usage
- Monthly income/expense charts (Chart.js)
- Cached statistics with automatic invalidation on new transactions
- Registration with auto-login and default categories
- Each user sees only their own data
- Protection against accessing other users' data (404)
- Full REST API with DRF
- Token + Session authentication
- Filtering, ordering, and pagination
- Income/expense/balance summary endpoint
- Interactive Swagger documentation
- Django 5.2 — Web framework
- Django REST Framework 3.15 — REST API
- PostgreSQL 16 — Primary database
- Redis 7 — Cache + Celery broker
- Celery — Background task processing
- django-filter — Declarative API filtering
- drf-spectacular — OpenAPI schema + Swagger UI
- djangorestframework-simplejwt — JWT authentication
- python-decouple — Environment configuration
- Bootstrap 5.3 — UI framework
- Bootstrap Icons — Icon set
- Chart.js — Statistics charts
- Docker & Docker Compose — Containerization (3 services)
- GitHub Actions — CI/CD (lint + test)
- Django TestCase — 26 tests
- black — Code formatter
- isort — Import sorter
- flake8 — Linter
- pre-commit — Automated code quality hooks
- Docker & Docker Compose
- Clone the repository:
git clone https://github.com/egorpusto/finance-manager.git
cd finance-manager/finance- Create
.envfile:
cp .env.example .env- Run with Docker:
docker compose up --buildThe app will be available at http://localhost:8000.
Migrations run automatically on startup.
- Create superuser:
docker compose exec web python manage.py createsuperuserdocker compose exec web python manage.py test transactions| Class | Tests |
|---|---|
CategoryModelTest |
3 |
TransactionModelTest |
4 |
BudgetUtilsTest |
4 |
TransactionListViewTest |
3 |
TransactionDetailViewTest |
2 |
CreateTransactionViewTest |
3 |
ExportTransactionsViewTest |
2 |
TransactionFormTest |
3 |
BudgetLimitFormTest |
2 |
| Total | 26 |
Base URL: /api/
Interactive docs: /api/docs/
| Endpoint | Methods | Description |
|---|---|---|
/api/transactions/ |
GET, POST | List / create transactions |
/api/transactions/{id}/ |
GET, PUT, PATCH, DELETE | Transaction detail |
/api/transactions/summary/ |
GET | Income / expense / balance summary |
/api/categories/ |
GET, POST | List / create categories |
/api/categories/{id}/ |
GET, PUT, PATCH, DELETE | Category detail |
/api/budgets/ |
GET, POST | List / create budget limits |
/api/budgets/{id}/ |
GET, PUT, PATCH, DELETE | Budget limit detail |
# Get token
curl -X POST http://localhost:8000/api/auth-token/ \
-d "username=user&password=pass"
# Use token
curl http://localhost:8000/api/transactions/ \
-H "Authorization: Token <your_token>"# Filter by type
GET /api/transactions/?type=expense
# Filter by category
GET /api/transactions/?category=1
# Filter by date range
GET /api/transactions/?date_from=2024-01-01&date_to=2026-01-31
# Order by amount descending
GET /api/transactions/?ordering=-amount
# Get balance summary
GET /api/transactions/summary/Date,Amount,Type,Category,Description
2026-03-01,1500.00,income,Salary,Monthly salary
2026-03-05,45.00,expense,Food,Groceries
2026-03-10,20.00,expense,Transport,Metro- Date —
YYYY-MM-DD - Type —
incomeorexpense - Category — created automatically if not exists
- Description — optional
| Variable | Description | Default |
|---|---|---|
SECRET_KEY |
Django secret key | — |
DEBUG |
Debug mode | False |
DB_NAME |
PostgreSQL database name | — |
DB_USER |
PostgreSQL user | — |
DB_PASSWORD |
PostgreSQL password | — |
DB_HOST |
PostgreSQL host | db |
DB_PORT |
PostgreSQL port | 5432 |
EMAIL_BACKEND |
Email backend | console |
EMAIL_HOST |
SMTP host | smtp.gmail.com |
EMAIL_PORT |
SMTP port | 587 |
EMAIL_USE_TLS |
Use TLS | True |
EMAIL_HOST_USER |
SMTP login | — |
EMAIL_HOST_PASSWORD |
SMTP password | — |
DEFAULT_FROM_EMAIL |
From address | — |
| Service | Image | Port |
|---|---|---|
web |
Custom (Django) | 8000 |
db |
postgres:16-alpine | 5432 |
redis |
redis:7-alpine | 6379 |
celery_worker |
Custom (Celery) | — |
# Start all services
docker compose up -d
# View logs
docker compose logs -f web
# Run tests
docker compose exec web python manage.py test transactions
# Run migrations manually
docker compose exec web python manage.py migrate
# Stop all services
docker compose down
# Reset database
docker compose down -v- id: BigInteger (Primary Key)
- user: ForeignKey → auth.User
- amount: Decimal(10, 2)
- date: Date
- type: VARCHAR(7) [income, expense]
- category: ForeignKey → Category (nullable)
- description: TEXT
- id: BigInteger (Primary Key)
- user: ForeignKey → auth.User
- name: VARCHAR(100)
- UniqueConstraint: (user, name)
- id: BigInteger (Primary Key)
- user: ForeignKey → auth.User
- category: ForeignKey → Category
- limit_amount: Decimal(10, 2)
- period: VARCHAR(10) [DAY, WEEK, MONTH]
- UniqueConstraint: (user, category, period)
finance/
├── finance/ # Project settings
│ ├── settings.py
│ ├── urls.py
│ ├── asgi.py
│ └── wsgi.py
├── transactions/ # Main app
│ ├── migrations/
│ ├── templates/
│ │ ├── registration/ # Auth templates
│ │ └── transactions/ # App templates
│ ├── static/
│ ├── models.py # Transaction, Category, BudgetLimit
│ ├── views.py # CBVs + DRF ViewSets
│ ├── serializers.py # DRF serializers
│ ├── forms.py # Django forms
│ ├── utils.py # Budget logic + CSV import
│ ├── signals.py # Cache invalidation + email alerts
│ ├── managers.py # Custom querysets
│ ├── admin.py
│ ├── mixins.py
│ ├── urls.py
│ ├── apps.py
│ └── tests.py # 26 tests
├── Dockerfile
├── docker-compose.yml
├── entrypoint.sh
├── requirements.txt
├── .env.example
├── .gitignore
└── README.md
Every push and pull request to main triggers:
✅ Lint — black, isort, flake8 ✅ Tests — Full test suite with PostgreSQL and Redis
- CSRF protection — All forms protected
- Login required — All views require authentication
- Object-level protection — Users can only access their own data (404 on foreign objects)
- Environment variables — Secrets never hardcoded
- Password validation — Django built-in validators
- Category ownership validation — API serializer validates category belongs to user
| Transactions | Statistics | Budget | Add Transaction |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
Egor Pusto
- GitHub: @egorpusto
This project is for educational purposes.



