Creating an advanced-level Personal Finance Manager project involves designing a robust database structure, implementing a Flask backend, and creating a responsive frontend using Bootstrap 5. Below is a detailed breakdown of the project:

1. Database Structure

The database will store user data, transactions, categories, budgets, and goals. We'll use SQLAlchemy (an ORM for Flask) to define the database models.

Database Models


from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
db = SQLAlchemy()
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
email = db.Column(db.String(100), unique=True, nullable=False)
password = db.Column(db.String(200), nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
transactions = db.relationship('Transaction', backref='user', lazy=True)
budgets = db.relationship('Budget', backref='user', lazy=True)
goals = db.relationship('Goal', backref='user', lazy=True)
class Transaction(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
category_id = db.Column(db.Integer, db.ForeignKey('category.id'), nullable=False)
amount = db.Column(db.Float, nullable=False)
description = db.Column(db.String(200))
date = db.Column(db.DateTime, default=datetime.utcnow)
type = db.Column(db.Enum('income', 'expense'), nullable=False)
class Category(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique=True, nullable=False)
transactions = db.relationship('Transaction', backref='category', lazy=True)
class Budget(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
category_id = db.Column(db.Integer, db.ForeignKey('category.id'), nullable=False)
amount = db.Column(db.Float, nullable=False)
start_date = db.Column(db.DateTime, default=datetime.utcnow)
end_date = db.Column(db.DateTime)
class Goal(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
name = db.Column(db.String(120), nullable=False)
target_amount = db.Column(db.Float, nullable=False)
current_amount = db.Column(db.Float, default=0.0)
deadline = db.Column(db.DateTime)

2. Flask Application Structure

Organize the Flask application into a modular structure:


personal_finance_manager/

├── app/
│ ├── __init__.py
│ ├── models.py
│ ├── routes/
│ │ ├── auth.py
│ │ ├── transactions.py
│ │ ├── budgets.py
│ │ ├── goals.py
│ │ └── dashboard.py
│ ├── templates/
│ │ ├── base.html
│ │ ├── auth/
│ │ │ ├── login.html
│ │ │ └── register.html
│ │ ├── transactions/
│ │ │ ├── add.html
│ │ │ └── list.html
│ │ ├── budgets/
│ │ │ ├── add.html
│ │ │ └── list.html
│ │ ├── goals/
│ │ │ ├── add.html
│ │ │ └── list.html
│ │ └── dashboard.html
│ ├── static/
│ │ ├── css/
│ │ │ └── styles.css
│ │ ├── js/
│ │ │ └── scripts.js
│ │ └── images/
│ └── config.py

├── requirements.txt
├── run.py
└── README.md

3. Flask Application Code

app/__init__.py


from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
db = SQLAlchemy()
login_manager = LoginManager()
def create_app():
app = Flask(__name__)
app.config.from_pyfile('config.py')
db.init_app(app)
login_manager.init_app(app)
from .routes.auth import auth_bp
from .routes.transactions import transactions_bp
from .routes.budgets import budgets_bp
from .routes.goals import goals_bp
from .routes.dashboard import dashboard_bp
app.register_blueprint(auth_bp)
app.register_blueprint(transactions_bp)
app.register_blueprint(budgets_bp)
app.register_blueprint(goals_bp)
app.register_blueprint(dashboard_bp)
return app

app/config.py


import os
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'a_secret_key'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///site.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False

run.py


from app import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True)

4. Flask Routes

app/routes/auth.py


from flask import Blueprint, render_template, redirect, url_for, flash
from flask_login import login_user, logout_user, login_required
from .forms import LoginForm, RegistrationForm
from ..models import User, db
auth_bp = Blueprint('auth', __name__)
@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and user.verify_password(form.password.data):
login_user(user)
return redirect(url_for('dashboard.index'))
flash('Login Unsuccessful. Please check email and password', 'danger')
return render_template('auth/login.html', form=form)
@auth_bp.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data, email=form.email.data)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
flash('Your account has been created! You can now log in', 'success')
return redirect(url_for('auth.login'))
return render_template('auth/register.html', form=form)
@auth_bp.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('auth.login'))

app/routes/transactions.py


from flask import Blueprint, render_template, redirect, url_for, flash
from flask_login import login_required, current_user
from ..models import Transaction, db
from .forms import TransactionForm
transactions_bp = Blueprint('transactions', __name__)
@transactions_bp.route('/transactions/add', methods=['GET', 'POST'])
@login_required
def add_transaction():
form = TransactionForm()
if form.validate_on_submit():
transaction = Transaction(user_id=current_user.id,
category_id=form.category.data,
amount=form.amount.data,
description=form.description.data,
date=form.date.data,
type=form.type.data)
db.session.add(transaction)
db.session.commit()
flash('Transaction added successfully!', 'success')
return redirect(url_for('transactions.list_transactions'))
return render_template('transactions/add.html', form=form)
@transactions_bp.route('/transactions')
@login_required
def list_transactions():
transactions = Transaction.query.filter_by(user_id=current_user.id).all()
return render_template('transactions/list.html', transactions=transactions)

app/routes/budgets.py


from flask import Blueprint, render_template, redirect, url_for, flash
from flask_login import login_required, current_user
from ..models import Budget, db
from .forms import BudgetForm
budgets_bp = Blueprint('budgets', __name__)
@budgets_bp.route('/budgets/add', methods=['GET', 'POST'])
@login_required
def add_budget():
form = BudgetForm()
if form.validate_on_submit():
budget = Budget(user_id=current_user.id,
category_id=form.category.data,
amount=form.amount.data,
start_date=form.start_date.data,
end_date=form.end_date.data)
db.session.add(budget)
db.session.commit()
flash('Budget created successfully!', 'success')
return redirect(url_for('budgets.list_budgets'))
return render_template('budgets/add.html', form=form)
@budgets_bp.route('/budgets')
@login_required
def list_budgets():
budgets = Budget.query.filter_by(user_id=current_user.id).all()
return render_template('budgets/list.html', budgets=budgets)

app/routes/goals.py


from flask import Blueprint, render_template, redirect, url_for, flash
from flask_login import login_required, current_user
from ..models import Goal, db
from .forms import GoalForm
goals_bp = Blueprint('goals', __name__)
@goals_bp.route('/goals/add', methods=['GET', 'POST'])
@login_required
def add_goal():
form = GoalForm()
if form.validate_on_submit():
goal = Goal(user_id=current_user.id,
name=form.name.data,
target_amount=form.target_amount.data,
current_amount=form.current_amount.data,
deadline=form.deadline.data)
db.session.add(goal)
db.session.commit()
flash('Goal created successfully!', 'success')
return redirect(url_for('goals.list_goals'))
return render_template('goals/add.html', form=form)
@goals_bp.route('/goals')
@login_required
def list_goals():
goals = Goal.query.filter_by(user_id=current_user.id).all()
return render_template('goals/list.html', goals=goals)

app/routes/dashboard.py


from flask import Blueprint, render_template
from flask_login import login_required, current_user
from ..models import Transaction, Budget, Goal
dashboard_bp = Blueprint('dashboard', __name__)
@dashboard_bp.route('/dashboard')
@login_required
def index():
transactions = Transaction.query.filter_by(user_id=current_user.id).all()
budgets = Budget.query.filter_by(user_id=current_user.id).all()
goals = Goal.query.filter_by(user_id=current_user.id).all()
return render_template('dashboard.html', transactions=transactions, budgets=budgets, goals=goals)

5. HTML Templates with Bootstrap 5

templates/base.html


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
<title>{% block title %}Personal Finance Manager{% endblock %}</title>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="{{ url_for('dashboard.index') }}">PFM</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{{ url_for('transactions.list_transactions') }}">Transactions</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('budgets.list_budgets') }}">Budgets</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('goals.list_goals') }}">Goals</a>
</li>
</ul>
<ul class="navbar-nav ms-auto">
{% if current_user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('auth.logout') }}">Logout</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('auth.login') }}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('auth.register') }}">Register</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-info">
{% for message in messages %}
<p>{{ message }}</p>
{% endfor %}
</div>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

templates/auth/login.html


{% extends 'base.html' %}
{% block title %}Login{% endblock %}
{% block content %}
<h2>Login</h2>
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.email.label(class="form-label") }}
{{ form.email(class="form-control") }}
</div>
<div class="mb-3">
{{ form.password.label(class="form-label") }}
{{ form.password(class="form-control") }}
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
{% endblock %}

templates/auth/register.html


{% extends 'base.html' %}
{% block title %}Register{% endblock %}
{% block content %}
<h2>Register</h2>
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.username.label(class="form-label") }}
{{ form.username(class="form-control") }}
</div>
<div class="mb-3">
{{ form.email.label(class="form-label") }}
{{ form.email(class="form-control") }}
</div>
<div class="mb-3">
{{ form.password.label(class="form-label") }}
{{ form.password(class="form-control") }}
</div>
<button type="submit" class="btn btn-primary">Register</button>
</form>
{% endblock %}

templates/transactions/add.html


{% extends 'base.html' %}
{% block title %}Add Transaction{% endblock %}
{% block content %}
<h2>Add Transaction</h2>
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.category.label(class="form-label") }}
{{ form.category(class="form-select") }}
</div>
<div class="mb-3">
{{ form.amount.label(class="form-label") }}
{{ form.amount(class="form-control") }}
</div>
<div class="mb-3">
{{ form.description.label(class="form-label") }}
{{ form.description(class="form-control") }}
</div>
<div class="mb-3">
{{ form.date.label(class="form-label") }}
{{ form.date(class="form-control") }}
</div>
<div class="mb-3">
{{ form.type.label(class="form-label") }}
{{ form.type(class="form-select") }}
</div>
<button type="submit" class="btn btn-primary">Add Transaction</button>
</form>
{% endblock %}

templates/transactions/list.html


{% extends 'base.html' %}
{% block title %}Transactions{% endblock %}
{% block content %}
<h2>Transactions</h2>
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Category</th>
<th>Description</th>
<th>Amount</th>
<th>Type</th>
</tr>
</thead>
<tbody>
{% for transaction in transactions %}
<tr>
<td>{{ transaction.date.strftime('%Y-%m-%d') }}</td>
<td>{{ transaction.category.name }}</td>
<td>{{ transaction.description }}</td>
<td>{{ transaction.amount }}</td>
<td>{{ transaction.type }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="{{ url_for('transactions.add_transaction') }}" class="btn btn-primary">Add Transaction</a>
{% endblock %}

templates/budgets/add.html


{% extends 'base.html' %}
{% block title %}Add Budget{% endblock %}
{% block content %}
<h2>Add Budget</h2>
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.category.label(class="form-label") }}
{{ form.category(class="form-select") }}
</div>
<div class="mb-3">
{{ form.amount.label(class="form-label") }}
{{ form.amount(class="form-control") }}
</div>
<div class="mb-3">
{{ form.start_date.label(class="form-label") }}
{{ form.start_date(class="form-control") }}
</div>
<div class="mb-3">
{{ form.end_date.label(class="form-label") }}
{{ form.end_date(class="form-control") }}
</div>
<button type="submit" class="btn btn-primary">Add Budget</button>
</form>
{% endblock %}

templates/budgets/list.html


{% extends 'base.html' %}
{% block title %}Budgets{% endblock %}
{% block content %}
<h2>Budgets</h2>
<table class="table">
<thead>
<tr>
<th>Category</th>
<th>Amount</th>
<th>Start Date</th>
<th>End Date</th>
</tr>
</thead>
<tbody>
{% for budget in budgets %}
<tr>
<td>{{ budget.category.name }}</td>
<td>{{ budget.amount }}</td>
<td>{{ budget.start_date.strftime('%Y-%m-%d') }}</td>
<td>{{ budget.end_date.strftime('%Y-%m-%d') if budget.end_date else 'N/A' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="{{ url_for('budgets.add_budget') }}" class="btn btn-primary">Add Budget</a>
{% endblock %}

templates/goals/add.html


{% extends 'base.html' %}
{% block title %}Add Goal{% endblock %}
{% block content %}
<h2>Add Goal</h2>
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.name.label(class="form-label") }}
{{ form.name(class="form-control") }}
</div>
<div class="mb-3">
{{ form.target_amount.label(class="form-label") }}
{{ form.target_amount(class="form-control") }}
</div>
<div class="mb-3">
{{ form.current_amount.label(class="form-label") }}
{{ form.current_amount(class="form-control") }}
</div>
<div class="mb-3">
{{ form.deadline.label(class="form-label") }}
{{ form.deadline(class="form-control") }}
</div>
<button type="submit" class="btn btn-primary">Add Goal</button>
</form>
{% endblock %}

templates/goals/list.html


{% extends 'base.html' %}
{% block title %}Goals{% endblock %}
{% block content %}
<h2>Goals</h2>
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Target Amount</th>
<th>Current Amount</th>
<th>Deadline</th>
</tr>
</thead>
<tbody>
{% for goal in goals %}
<tr>
<td>{{ goal.name }}</td>
<td>{{ goal.target_amount }}</td>
<td>{{ goal.current_amount }}</td>
<td>{{ goal.deadline.strftime('%Y-%m-%d') if goal.deadline else 'N/A' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="{{ url_for('goals.add_goal') }}" class="btn btn-primary">Add Goal</a>
{% endblock %}

templates/dashboard.html


{% extends 'base.html' %}
{% block title %}Dashboard{% endblock %}
{% block content %}
<h2>Dashboard</h2>
<div class="row">
<div class="col-md-4">
<h3>Transactions</h3>
<ul class="list-group">
{% for transaction in transactions %}
<li class="list-group-item">
{{ transaction.date.strftime('%Y-%m-%d') }} - {{ transaction.description }}: {{ transaction.amount }} ({{ transaction.type }})
</li>
{% endfor %}
</ul>
<a href="{{ url_for('transactions.add_transaction') }}" class="btn btn-primary">Add Transaction</a>
</div>
<div class="col-md-4">
<h3>Budgets</h3>
<ul class="list-group">
{% for budget in budgets %}
<li class="list-group-item">
{{ budget.category.name }}: {{ budget.amount }} ({{ budget.start_date.strftime('%Y-%m-%d') }} - {{ budget.end_date.strftime('%Y-%m-%d') if budget.end_date else 'N/A' }})
</li>
{% endfor %}
</ul>
<a href="{{ url_for('budgets.add_budget') }}" class="btn btn-primary">Add Budget</a>
</div>
<div class="col-md-4">
<h3>Goals</h3>
<ul class="list-group">
{% for goal in goals %}
<li class="list-group-item">
{{ goal.name }}: {{ goal.current_amount }} / {{ goal.target_amount }} (Deadline: {{ goal.deadline.strftime('%Y-%m-%d') if goal.deadline else 'N/A' }})
</li>
{% endfor %}
</ul>
<a href="{{ url_for('goals.add_goal') }}" class="btn btn-primary">Add Goal</a>
</div>
</div>
{% endblock %}

Requirements


Flask==2.0.1
Flask-SQLAlchemy==2.5.1
Flask-Login==0.5.0
WTForms==2.3.3

Conclusion

This structure provides a comprehensive foundation for a Personal Finance Manager application using Flask and Bootstrap 5. You can expand upon this by adding features such as data visualization, user settings, and more advanced financial analytics.