Build a PinFood application with Flask (part 1)

Brojen Ais
Try Full Stack
Published in
6 min readJan 24, 2021

Pin Food is a web application that displays a page containing an interactive map. The user can mark the location of a new “food stall” on the map. Users can enter the date, category, and description of the “food stall”.

Users can also see places for food stalls that have been marked before by displaying an icon on the map and if the user hovers the mouse over an icon, a more complete description of the food stall will appear.

The goal is, users can easily see which areas have food stalls according to category and help Vloggers come to these places to create their own YouTube content.

Pin Food Application

In this section we will set up a development environment for our project, create a virtual environment and activate it, install Flask and PyMySQL, configure Flask for a MySQL Database and try to build our first page.

Preparation

Open a terminal or command line, then enter into any drive (for example, drive D if you are using windows). Create a folder called pinfood:

mkdir pinfood
cd pinfood

Create a virtual environment named .venv and activate it:

python -m venv .venv
.venv\Scripts\activate

Install the PyMySQL and Flask packages. PyMySQL is used to communicate with MySQL:

pip install flask pymysql

Database Setup

Inside the project root, create a file called dbconf.py and add the following configuration:

test = True

dbuser = 'root'
dbpassword = ''

Like Django, we use dbconf.py file to store settings for our project. test = True, meaning that our project is currently in development mode. dbuser contains the MySQL username and dbpassword contains the password to access the MySQL database.

Let’s create a file called dbsetup.py inside the project root. Then, type the following code:

import pymysql
import dbconf

connection = pymysql.connect(host='localhost',
user=dbconf.dbuser,
passwd=dbconf.dbpassword)

try:
with connection.cursor() as cursor:
sql = "CREATE DATABASE IF NOT EXISTS db_pinfood"
cursor.execute(sql)

sql = """CREATE TABLE IF NOT EXISTS db_pinfood.foods (
id int NOT NULL AUTO_INCREMENT,
latitude FLOAT(10,6),
longitude FLOAT(10,6),
date DATETIME,
category VARCHAR(50),
description VARCHAR(1000),
updated_at TIMESTAMP,
PRIMARY KEY (id)
)"""

cursor.execute(sql)

connection.commit()

finally:
connection.close()

The dbsetup.py file contains the codes for creating databases and tables. We need this file so that we no longer create databases and tables manually in the MySQL console. I think this will help us, if we really don’t want to open such a boring MySQL console.

Let’s turn on MySQL Server. Then, run dbsetup.py to create database and table automatically:

python dbsetup.py

Make sure the database is created successfully by viewing it in the MySQL console using SHOW DATABASES; or you can use PHPMyAdmin and also, make sure the foods table in the db_pinfood is created as well:

image 01

In your favorite editor (inside the root project folder), create a directory structure and files like:

image 02

Then, let’s open the dbhelper.py file and add the following code:

import pymysql
import dbconf


class DBHelper:

def connect(self, database="db_pinfood"):
return pymysql.connect(host='localhost',
user=dbconf.dbuser,
passwd=dbconf.dbpassword,
db=database)

def get_all_inputs(self):
connection = self.connect()

try:
query = "SELECT description FROM foods;"
with connection.cursor() as cursor:
cursor.execute(query)
return cursor.fetchall()

finally:
connection.close()

def add_input(self, data):
connection = self.connect()
try:
# The following introduces a deliberate security
# flaw. See section on SQL injection below
query = "INSERT INTO foods " \
"(description) VALUES " \
"('{}');".format(data)
with connection.cursor() as cursor:
cursor.execute(query)
connection.commit()

finally:
connection.close()

def clear_all(self):
connection = self.connect()

try:
query = "DELETE FROM foods;"
with connection.cursor() as cursor:
cursor.execute(query)
connection.commit()
finally:
connection.close()

The dbhelper.py file contains the DBHelper class with methods to help us get all the data, add data to the database, and delete data from the database. Just Just imagine this is a model. This model is used as an interface for our application to communicate with the database.

Next, open the pinfood.py file and add the following code:

from dbhelper import DBHelper
from flask import Flask
from flask import render_template
from flask import request

app = Flask(__name__)
DB = DBHelper()


@app.route("/")
def home():
try:
data = DB.get_all_inputs()
except Exception as e:
print(e)
data = None
return render_template("home.html", data=data)


@app.route("/add", methods=["POST"])
def add():
try:
data = request.form.get("description")
DB.add_input(data)
except Exception as e:
print(e)
return home()


@app.route("/clear")
def clear():
try:
DB.clear_all()

except Exception as e:
print(e)

return home()


if __name__ == '__main__':
app.run(port=5000, debug=True)

The pinfood.py file is the main file for our project. We can run our project by running this file. When this file is executed, Flask will run a development server which we can access on localhost with port 5000.

Next, open the templates/home.html file and add the following code:

<!DOCTYPE html>
<html lang="en">
<head>
<title>Pin Food</title>
</head>
<body>
<div class="container mt-4 mb-3">
<div class="col-md-12">
<h1 class="display-4">Pin Food</h1>
<hr>
</div>
</div>
<div class="container mb-3">
<div class="col-md-4">
<form action="/add" method="POST">
<label>Description</label>
<input type="text" class="form-control"
name="description">
<button class="btn btn-primary" type="submit">
Submit
</button>
<a href="/clear" class="btn btn-outline-danger">
Clear
</a>
</form>
<div class="mt-3">
{% if data %}
{% for description in data %}
<p>{{ description }}</p>
{% endfor %}
{% endif %}
</div>
</div>
</div>
</body>
</html>

The file is a template containing HTML codes. Flask will render the data passed from the home function in the pinfood.py file to this template.

action=”/add” is the url, which points to the add function inside the pinfood.py file. If this form is submitted, the data will be processed in the add function.

Also, href = “/ clear” is the url, which points to the clear function inside pinfood.py. The clear function will delete existing data when this button is clicked. Be careful.

Then, on the following line:

<div class="mt-3">
{% if data %}
{% for description in data %}
<p>{{ description }}</p>
{% endfor %}
{% endif %}
</div>

The data passed from the home function will be checked with the jinja template engine using if block. If the data is not empty or None, the next block will be executed. The next block is looping using the for.

Alright, we will run the project with the following command:

python pinfood.py

Now, open http://localhost:5000/ in a browser and you should see a result like this:

image 03

You can try it, by entering any data into the description field and clicking the Submit button. Try it several times! And then, if you check the foods table you will see the data that was successfully saved:

image 04

Source Code

We provide source code that you can clone at any time at the following link:

--

--

Brojen Ais
Try Full Stack
0 Followers
Editor for

I’m the author of “Try Full Stack” and a coffee addict. Happy to share positive things.