Creating an advanced-level Bus Booking System with Flask and Bootstrap 5 involves designing a robust database structure, implementing backend logic, and creating responsive frontend pages. Below is a detailed implementation:

1. Database Structure

The database will be designed using SQLAlchemy (ORM for Flask). Here's the schema:

Tables

  • Users: Stores user information.
  • Buses: Stores bus details.
  • Routes: Stores route information.
  • Bookings: Stores booking details.
  • Payments: Stores payment details.

SQL Schema


CREATE TABLE Users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
role ENUM('admin', 'manager', 'sales') NOT NULL
);
CREATE TABLE Brands (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) UNIQUE NOT NULL
);
CREATE TABLE Models (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
brand_id INT,
FOREIGN KEY (brand_id) REFERENCES Brands(id)
);
CREATE TABLE Cars (
id INT PRIMARY KEY AUTO_INCREMENT,
model_id INT,
year INT NOT NULL,
color VARCHAR(50),
price DECIMAL(10, 2) NOT NULL,
mileage INT,
FOREIGN KEY (model_id) REFERENCES Models(id)
);
CREATE TABLE Inventory (
id INT PRIMARY KEY AUTO_INCREMENT,
car_id INT UNIQUE,
quantity INT DEFAULT 0,
FOREIGN KEY (car_id) REFERENCES Cars(id)
);
CREATE TABLE Customers (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
phone VARCHAR(15)
);
CREATE TABLE Sales (
id INT PRIMARY KEY AUTO_INCREMENT,
car_id INT,
customer_id INT,
sale_date DATE NOT NULL,
sale_price DECIMAL(10, 2) NOT NULL,
FOREIGN KEY (car_id) REFERENCES Cars(id),
FOREIGN KEY (customer_id) REFERENCES Customers(id)
);

2. Flask Application Structure

The Flask application will have the following structure:


bus_booking_system/

├── app.py
├── models.py
├── forms.py
├── templates/
│ ├── base.html
│ ├── index.html
│ ├── login.html
│ ├── register.html
│ ├── search_buses.html
│ ├── booking.html
│ ├── payment.html
│ ├── dashboard.html
│ └── admin/
│ ├── add_bus.html
│ ├── manage_buses.html
│ └── manage_routes.html
├── static/
│ ├── css/
│ │ └── styles.css
│ ├── js/
│ │ └── scripts.js
│ └── images/
└── requirements.txt

3. Flask Application Code

app.py


from flask import Flask, render_template, request, redirect, url_for, flash, session
from flask_sqlalchemy import SQLAlchemy
from models import db, User, Bus, Route, Booking, Payment
from forms import LoginForm, RegistrationForm, BookingForm, PaymentForm
from datetime import datetime
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///bus_booking.db'
db.init_app(app)
# Routes
@app.route('/')
def index():
return render_template('index.html')
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data, email=form.email.data, password=form.password.data)
db.session.add(user)
db.session.commit()
flash('Registration successful! Please login.', 'success')
return redirect(url_for('login'))
return render_template('register.html', form=form)
@app.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.password == form.password.data: # Use hashed passwords in production
session['user_id'] = user.id
flash('Login successful!', 'success')
return redirect(url_for('dashboard'))
flash('Login failed. Check your email and password.', 'danger')
return render_template('login.html', form=form)
@app.route('/dashboard')
def dashboard():
if 'user_id' not in session:
return redirect(url_for('login'))
user = User.query.get(session['user_id'])
return render_template('dashboard.html', user=user)
@app.route('/search_buses', methods=['GET', 'POST'])
def search_buses():
if request.method == 'POST':
source = request.form['source']
destination = request.form['destination']
buses = Bus.query.join(Route).filter(Route.source == source, Route.destination == destination).all()
return render_template('search_buses.html', buses=buses)
return render_template('search_buses.html', buses=[])
@app.route('/book/<int:bus_id>', methods=['GET', 'POST'])
def book(bus_id):
if 'user_id' not in session:
return redirect(url_for('login'))
form = BookingForm()
bus = Bus.query.get(bus_id)
if form.validate_on_submit():
booking = Booking(user_id=session['user_id'], bus_id=bus_id, seats_booked=form.seats_booked.data)
bus.available_seats -= form.seats_booked.data
db.session.add(booking)
db.session.commit()
flash('Booking successful! Proceed to payment.', 'success')
return redirect(url_for('payment', booking_id=booking.id))
return render_template('booking.html', form=form, bus=bus)
@app.route('/payment/<int:booking_id>', methods=['GET', 'POST'])
def payment(booking_id):
if 'user_id' not in session:
return redirect(url_for('login'))
form = PaymentForm()
booking = Booking.query.get(booking_id)
if form.validate_on_submit():
payment = Payment(booking_id=booking.id, amount=form.amount.data, status='Completed')
db.session.add(payment)
db.session.commit()
flash('Payment successful!', 'success')
return redirect(url_for('dashboard'))
return render_template('payment.html', form=form, booking=booking)
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run(debug=True)

models.py


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)
bookings = db.relationship('Booking', backref='user', lazy=True)
class Bus(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
total_seats = db.Column(db.Integer, nullable=False)
available_seats = db.Column(db.Integer, nullable=False)
route_id = db.Column(db.Integer, db.ForeignKey('route.id'), nullable=False)
bookings = db.relationship('Booking', backref='bus', lazy=True)
class Route(db.Model):
id = db.Column(db.Integer, primary_key=True)
source = db.Column(db.String(100), nullable=False)
destination = db.Column(db.String(100), nullable=False)
distance = db.Column(db.Float, nullable=False)
buses = db.relationship('Bus', backref='route', lazy=True)
class Booking(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
bus_id = db.Column(db.Integer, db.ForeignKey('bus.id'), nullable=False)
seats_booked = db.Column(db.Integer, nullable=False)
booking_date = db.Column(db.DateTime, default=datetime.utcnow)
payment = db.relationship('Payment', backref='booking', uselist=False)
class Payment(db.Model):
id = db.Column(db.Integer, primary_key=True)
booking_id = db.Column(db.Integer, db.ForeignKey('booking.id'), nullable=False)
amount = db.Column(db.Float, nullable=False)
payment_date = db.Column(db.DateTime, default=datetime.utcnow)
status = db.Column(db.String(20), default='Pending')

forms.py


from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, IntegerField, FloatField, SubmitField
from wtforms.validators import DataRequired, Email, Length, NumberRange
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=2, max=50)])
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired(), Length(min=6, max=200)])
submit = SubmitField('Register')
class LoginForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
submit = SubmitField('Login')
class BookingForm(FlaskForm):
seats_booked = IntegerField('Seats to Book', validators=[DataRequired(), NumberRange(min=1)])
submit = SubmitField('Book Now')
class PaymentForm(FlaskForm):
amount = FloatField('Amount', validators=[DataRequired(), NumberRange(min=0.01)])
submit = SubmitField('Pay Now')

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 %}Bus Booking System{% 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('index') }}">Bus Booking</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" href="{{ url_for('search_buses') }}">Search Buses</a>
</li>
{% if 'user_id' in session %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('dashboard') }}">Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('logout') }}">Logout</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('login') }}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('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 %}
{{ message }}<br>
{% 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/index.html


{% extends 'base.html' %}
{% block content %}
<h1>Welcome to the Bus Booking System</h1>
<p>Find and book your bus tickets easily!</p>
<a href="{{ url_for('search_buses') }}" class="btn btn-primary">Search Buses</a>
{% endblock %}

templates/search_buses.html


{% extends 'base.html' %}
{% block content %}
<h2>Search Buses</h2>
<form method="POST">
<div class="mb-3">
<label for="source" class="form-label">Source</label>
<input type="text" class="form-control" name="source" required>
</div>
<div class="mb-3">
<label for="destination" class="form-label">Destination</label>
<input type="text" class="form-control" name="destination" required>
</div>
<button type="submit" class="btn btn-primary">Search</button>
</form>
{% if buses %}
<h3>Available Buses</h3>
<ul class="list-group mt-3">
{% for bus in buses %}
<li class="list-group-item">
{{ bus.name }} - Available Seats: {{ bus.available_seats }}
<a href="{{ url_for('book', bus_id=bus.id) }}" class="btn btn-success btn-sm float-end">Book Now</a>
</li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}

templates/booking.html


{% extends 'base.html' %}
{% block content %}
<h2>Booking for {{ bus.name }}</h2>
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.seats_booked.label(class="form-label") }}
{{ form.seats_booked(class="form-control") }}
</div>
{{ form.submit(class="btn btn-primary") }}
</form>
{% endblock %}

templates/payment.html


{% extends 'base.html' %}
{% block content %}
<h2>Payment for Booking ID: {{ booking.id }}</h2>
<p>Amount: ${{ booking.seats_booked * 10 }} (Assuming $10 per seat)</p>
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.amount.label(class="form-label") }}
{{ form.amount(class="form-control") }}
</div>
{{ form.submit(class="btn btn-primary") }}
</form>
{% endblock %}

templates/dashboard.html


{% extends 'base.html' %}
{% block content %}
<h2>Dashboard</h2>
<p>Welcome, {{ user.username }}!</p>
<h3>Your Bookings</h3>
<ul class="list-group">
{% for booking in user.bookings %}
<li class="list-group-item">
Booking ID: {{ booking.id }} - Bus: {{ booking.bus.name }} - Seats: {{ booking.seats_booked }}
</li>
{% endfor %}
</ul>
{% endblock %}

templates/admin/add_bus.html


{% extends 'base.html' %}
{% block content %}
<h2>Add New Bus</h2>
<form method="POST">
<div class="mb-3">
<label for="name" class="form-label">Bus Name</label>
<input type="text" class="form-control" name="name" required>
</div>
<div class="mb-3">
<label for="total_seats" class="form-label">Total Seats</label>
<input type="number" class="form-control" name="total_seats" required>
</div>
<div class="mb-3">
<label for="route_id" class="form-label">Route ID</label>
<input type="number" class="form-control" name="route_id" required>
</div>
<button type="submit" class="btn btn-primary">Add Bus</button>
</form>
{% endblock %}

templates/admin/manage_buses.html


{% extends 'base.html' %}
{% block content %}
<h2>Manage Buses</h2>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Total Seats</th>
<th>Available Seats</th>
<th>Route</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for bus in buses %}
<tr>
<td>{{ bus.id }}</td>
<td>{{ bus.name }}</td>
<td>{{ bus.total_seats }}</td>
<td>{{ bus.available_seats }}</td>
<td>{{ bus.route.source }} to {{ bus.route.destination }}</td>
<td>
<a href="{{ url_for('edit_bus', bus_id=bus.id) }}" class="btn btn-warning btn-sm">Edit</a>
<a href="{{ url_for('delete_bus', bus_id=bus.id) }}" class="btn btn-danger btn-sm">Delete</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

templates/admin/manage_routes.html


{% extends 'base.html' %}
{% block content %}
<h2>Manage Routes</h2>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Source</th>
<th>Destination</th>
<th>Distance (km)</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for route in routes %}
<tr>
<td>{{ route.id }}</td>
<td>{{ route.source }}</td>
<td>{{ route.destination }}</td>
<td>{{ route.distance }}</td>
<td>
<a href="{{ url_for('edit_route', route_id=route.id) }}" class="btn btn-warning btn-sm">Edit</a>
<a href="{{ url_for('delete_route', route_id=route.id) }}" class="btn btn-danger btn-sm">Delete</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

Conclusion

This structure provides a comprehensive foundation for a Car Sales and Inventory Store project using Flask and Bootstrap 5. You can expand upon this by adding features such as search functionality, user permissions, and more advanced reporting.