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.