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.