Building Simple Shopping Cart using Python, Flask, MySQL

Introduction

In this tutorial I will show you here how to build a simple shopping cart using Python, Flask, MySQL. This shopping cart is very simple and it does not have checkout option, payment option. This is about to display products in an online shopping portal, add item to cart, remove item from cart and remove all items from cart at once or empty cart.

Prerequisites

Python 3.7.4 – 3.9.1, MySQL 8.0.17 – 8.0.22, Flask 1.1.1 – 1.1.2

Need flask_table and MySQL modules : Check tutorial for installation these two modules

Project Directory

First step is to create a project root directory under which you will put all your required files for the project.

Let’s say I am going to create a project root directory python_flask_mysql_shopping_cart.

I may not mention the project root directory in subsequent sections and I will assume that I am talking with respect to the project’s root directory.

MySQL Table

Create a table product under database roytuts in MySQL server with the following structure:

CREATE TABLE `product` (
	`id` int unsigned COLLATE utf8mb4_unicode_ci NOT NULL AUTO_INCREMENT,
	`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
	`code` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
	`image` text COLLATE utf8mb4_unicode_ci NOT NULL,
	`price` double COLLATE utf8mb4_unicode_ci NOT NULL,
	PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4;

I will test the application when I finish with coding, so I am dumping some data into product table.

INSERT INTO `product` (`id`, `name`, `code`, `image`, `price`) VALUES
(1, 'American Tourist', 'AMTR01', 'product-images/bag.jpg', 12000.00),
(2, 'EXP Portable Hard Drive', 'USB02', 'product-images/external-hard-drive.jpg', 5000.00),
(3, 'Shoes', 'SH03', 'product-images/shoes.jpg', 1000.00),
(4, 'XP 1155 Intel Core Laptop', 'LPN4', 'product-images/laptop.jpg', 80000.00),
(5, 'FinePix Pro2 3D Camera', '3DCAM01', 'product-images/camera.jpg', 150000.00),
(6, 'Simple Mobile', 'MB06', 'product-images/mobile.jpg', 3000.00),
(7, 'Luxury Ultra thin Wrist Watch', 'WristWear03', 'product-images/watch.jpg', 3000.00),
(8, 'Headphone', 'HD08', 'product-images/headphone.jpg', 400.00);

Configuring Flask

Create the below app.py script (py is the extension to indicate Python script) where I import the flask module. This file should be created under the project root directory. Notice how I create flask instance. I have configured a secret key, which is required for your application’s session.

from flask import Flask

app = Flask(__name__)
app.secret_key = "secret key"

Database Configuration

I create the below db_config.py Python script under project root directory to setup the MySQL database configurations for connecting to database.

I need to configure database connection with flask module and that’s why I have imported app module and setup the MySQL configuration with flask module.

from app import app
from flaskext.mysql import MySQL

mysql = MySQL()
 
# MySQL configurations
app.config['MYSQL_DATABASE_USER'] = 'root'
app.config['MYSQL_DATABASE_PASSWORD'] = 'root'
app.config['MYSQL_DATABASE_DB'] = 'roytuts'
app.config['MYSQL_DATABASE_HOST'] = 'localhost'
mysql.init_app(app)

Configuring URLs

Next I will create main.py script that will define all URIs or Action paths for performing certain operations used for cart or to fetch all products from the database.

It will connect to MySQL database server and query the database to read all products from the database table.

In this script I perform certain operations with user interaction.

I first import required modules into the script. I then define the end-point / for displaying a view where user will see all the products.

Next I need to validate user input data and save those input data into into cart, i.e., flask session. So I define another end-point /add. Here I fetch the data based on product’s code from MySQL database and put into session against unique product keys.

I use http method GET for displaying view and POST method for sending data to server side. By default http method is GET if you do not specify http method.

I use render_template function from flask to show the view.

Initially when there is no product or item in the cart then you won’t see any item or product in the cart on home page or products page.

If you add product to cart from Add to Cart link shown on each product then you will see product gets added and displayed into the cart.

I define /empty endpoint to clear all items from the cart.

Next I define /delete/<string:code> end-point where user deletes product one by one from the cart.

import pymysql
from app import app
from db_config import mysql
from flask import flash, session, render_template, request, redirect, url_for
#from werkzeug import generate_password_hash, check_password_hash
from werkzeug.security import generate_password_hash, check_password_hash
		
@app.route('/add', methods=['POST'])
def add_product_to_cart():
	cursor = None
	try:
		_quantity = int(request.form['quantity'])
		_code = request.form['code']
		# validate the received values
		if _quantity and _code and request.method == 'POST':
			conn = mysql.connect()
			cursor = conn.cursor(pymysql.cursors.DictCursor)
			cursor.execute("SELECT * FROM product WHERE code=%s", _code)
			row = cursor.fetchone()
			
			itemArray = { row['code'] : {'name' : row['name'], 'code' : row['code'], 'quantity' : _quantity, 'price' : row['price'], 'image' : row['image'], 'total_price': _quantity * row['price']}}
			
			all_total_price = 0
			all_total_quantity = 0
			
			session.modified = True
			if 'cart_item' in session:
				if row['code'] in session['cart_item']:
					for key, value in session['cart_item'].items():
						if row['code'] == key:
							#session.modified = True
							#if session['cart_item'][key]['quantity'] is not None:
							#	session['cart_item'][key]['quantity'] = 0
							old_quantity = session['cart_item'][key]['quantity']
							total_quantity = old_quantity + _quantity
							session['cart_item'][key]['quantity'] = total_quantity
							session['cart_item'][key]['total_price'] = total_quantity * row['price']
				else:
					session['cart_item'] = array_merge(session['cart_item'], itemArray)

				for key, value in session['cart_item'].items():
					individual_quantity = int(session['cart_item'][key]['quantity'])
					individual_price = float(session['cart_item'][key]['total_price'])
					all_total_quantity = all_total_quantity + individual_quantity
					all_total_price = all_total_price + individual_price
			else:
				session['cart_item'] = itemArray
				all_total_quantity = all_total_quantity + _quantity
				all_total_price = all_total_price + _quantity * row['price']
			
			session['all_total_quantity'] = all_total_quantity
			session['all_total_price'] = all_total_price
			
			return redirect(url_for('.products'))
		else:			
			return 'Error while adding item to cart'
	except Exception as e:
		print(e)
	finally:
		cursor.close() 
		conn.close()
		
@app.route('/')
def products():
	try:
		conn = mysql.connect()
		cursor = conn.cursor(pymysql.cursors.DictCursor)
		cursor.execute("SELECT * FROM product")
		rows = cursor.fetchall()
		return render_template('products.html', products=rows)
	except Exception as e:
		print(e)
	finally:
		cursor.close() 
		conn.close()

@app.route('/empty')
def empty_cart():
	try:
		session.clear()
		return redirect(url_for('.products'))
	except Exception as e:
		print(e)

@app.route('/delete/<string:code>')
def delete_product(code):
	try:
		all_total_price = 0
		all_total_quantity = 0
		session.modified = True
		
		for item in session['cart_item'].items():
			if item[0] == code:				
				session['cart_item'].pop(item[0], None)
				if 'cart_item' in session:
					for key, value in session['cart_item'].items():
						individual_quantity = int(session['cart_item'][key]['quantity'])
						individual_price = float(session['cart_item'][key]['total_price'])
						all_total_quantity = all_total_quantity + individual_quantity
						all_total_price = all_total_price + individual_price
				break
		
		if all_total_quantity == 0:
			session.clear()
		else:
			session['all_total_quantity'] = all_total_quantity
			session['all_total_price'] = all_total_price
		
		#return redirect('/')
		return redirect(url_for('.products'))
	except Exception as e:
		print(e)
		
def array_merge( first_array , second_array ):
	if isinstance( first_array , list ) and isinstance( second_array , list ):
		return first_array + second_array
	elif isinstance( first_array , dict ) and isinstance( second_array , dict ):
		return dict( list( first_array.items() ) + list( second_array.items() ) )
	elif isinstance( first_array , set ) and isinstance( second_array , set ):
		return first_array.union( second_array )
	return False		
		
if __name__ == "__main__":
    app.run()

Template File

Now create products.html file and put it under templates directory.

Notice how I am using flask EL expression to use variable to show data into HTML file.

I have added a link using which a user will be able to empty cart and this link is displayed only when at least one product is added to the cart.

I also check for any success or error message and display them. I display messages from flash scope and for this I need session and for session I need secret key and that’s why I have configured Secret Key in app.py script.

Finally I show the products into HTML page.

<!DOCTYPE html>
<html>
<head>
	<title>Python Flask Simple Shopping Cart</title>
	<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet" type="text/css" />
</head>
<body>
	<div>
		{% with messages = get_flashed_messages() %}
		  {% if messages %}
			<ul class=flashes>
			{% for message in messages %}
			  <li>{{ message }}</li>
			{% endfor %}
			</ul>
		  {% endif %}
		{% endwith %}
	</div>

	<div id="shopping-cart">
		<div class="txt-heading">Shopping Cart</div>		
		{% if 'cart_item' in session %}
			<a id="btnEmpty" href="{{ url_for('.empty_cart') }}">Empty Cart</a>
			<table class="tbl-cart" cellpadding="10" cellspacing="1">
			<tbody>
				<tr>
					<th style="text-align:left;">Name</th>
					<th style="text-align:left;">Code</th>
					<th style="text-align:right;" width="5%">Quantity</th>
					<th style="text-align:right;" width="10%">Unit Price</th>
					<th style="text-align:right;" width="10%">Price</th>
					<th style="text-align:center;" width="5%">Remove</th>
				</tr>
				{% for key, val in session['cart_item'].items() %}
					{% set quantity = session['cart_item'][key]['quantity'] %}
					{% set price = session['cart_item'][key]['price'] %}
					{% set item_price = session['cart_item'][key]['total_price'] %}					
					<tr>
						<td><img src="/static/images/{{ session['cart_item'][key]['image'] }}" class="cart-item-image" />{{ session['cart_item'][key]['name'] }}</td>
						<td>{{ session['cart_item'][key]['code'] }}</td>
						<td style="text-align:right;">{{ quantity }}</td>
						<td  style="text-align:right;">₹ {{ price }}</td>
						<td  style="text-align:right;">₹ {{ item_price }}</td>
						<td style="text-align:center;">
							<a href="{{ url_for('.delete_product', code=session['cart_item'][key]['code']) }}" class="btnRemoveAction">
								<img src="/static/images/icon-delete.png" alt="Remove Item" />
							</a>
						</td>
					</tr>
				{% endfor %}
				<tr>
					<td colspan="2" align="right">Total:</td>
					<td align="right">{{ session['all_total_quantity'] }}</td>
					<td align="right" colspan="2"><strong>₹ {{ session['all_total_price'] }}</strong></td>
					<td></td>
				</tr>
			</tbody>
			</table>
		{% else: %}
			<div class="no-records">Your Cart is Empty</div>
		{% endif %}
	</div>

	<div id="product-grid">
		<div class="txt-heading">Products</div>

		{% for product in products %}

			<div class="product-item">
				<form method="post" action="/add">
					<div class="product-image"><img src="/static/images/{{ product.image }}"></div>
					<div class="product-tile-footer">
						<div class="product-title">{{ product.name }}</div>
						<div class="product-price">₹ {{ product.price }}</div>
						<div class="cart-action">
							<input type="hidden" name="code" value="{{ product.code }}"/>
							<input type="text" class="product-quantity" name="quantity" value="1" size="2" />
							<input type="submit" value="Add to Cart" class="btnAddAction" />
						</div>
					</div>
				</form>
			</div>

		{% endfor %}
	
	</div>
</body>
</html>

Testing the Application

Now navigate to the project root directory from command line tool and execute the command python main.py or simply main.py, your server will start on default port 5000.

If you want to change the port then you can change the line app.run() to app.run(port=50001), where 5001 is the new port.

Home Page

Hit URL http://localhost:5000/ in the browser, you will see below output on the browser:

simple shopping cart using python flask mysql

Cart Items

Once you add some items to the cart, you will see those items in the cart:

simple shopping cart using python flask mysql

That’s all about how to build simple shopping cart using Python Flask and MySQL.

Source Code

Download

Leave a Reply

Your email address will not be published. Required fields are marked *