Flask Projects With Source Code

Personal Finance Manager Project In Flask


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.

Written by Surfside Media

Senior Full Stack Developer specializing in Web Technologies.