Advertisement
  1. Code
  2. Coding Fundamentals
  3. Databases & SQL

Creating a Web App From Scratch Using Python Flask and MySQL: Part 2

Scroll to top
This post is part of a series called Creating a Web App From Scratch Using Python Flask and MySQL.
Creating a Web App From Scratch Using Python Flask and MySQL
Creating a Web App From Scratch Using Python Flask and MySQL: Part 3

In the previous part of this series, we saw how to get started with Python Flask and MySQL and implemented the user registration portion of our application. In this tutorial, we'll take this to the next level by implementing the sign-in and logout functionality for our application.

Getting Started

First clone the source code of the previous tutorial from GitHub.

1
git clone https://github.com/tutsplus/create-a-web-app-from-scratch-using-python-flask-and-mysql/.git

Once the source code has been cloned, navigate to the part-1 directory and start the server.

1
python app.py

Point your browser to https://localhost:5000 and you should have the application running.

Creating the Sign-In Interface

Navigate to FlaskApp/templates and create a new file called signin.html. Open signin.html and add the following HTML code:

1
<!DOCTYPE html>
2
<html lang="en">
3
    <head>
4
		    
5
		<title>Python Flask Bucket List App - Sign In</title>
6
		    
7
		<link
8
			href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
9
			rel="stylesheet"
10
			integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
11
			crossorigin="anonymous"
12
		/>
13
		<link href="../static/signup.css" rel="stylesheet" />
14
	</head>
15
16
	<body>
17
		    
18
		<div class="container">
19
			        
20
			<div class="header">
21
				            
22
				<nav
23
					class="border-bottom flex-wrap mb-4 py-3 d-flex justify-content-center"
24
				>
25
					<a
26
						href="/"
27
						class="text-dark text-decoration-none mb-3 mb-md-0 d-flex align-items-center me-md-auto"
28
					>
29
						<span class="fs-4">Python Flask App</span>
30
					</a>
31
32
					<ul class="nav nav-pills">
33
						<li class="nav-item">
34
							<a href="/" class="nav-link">Home</a>
35
						</li>
36
						<li class="nav-item">
37
							<a href="/signup" class="nav-link">Signup</a>
38
						</li>
39
						<li class="nav-item">
40
							<a href="/signin" class="nav-link active" aria-current="page"
41
								>Sign In</a
42
							>
43
						</li>
44
					</ul>
45
					                        
46
				</nav>
47
				        
48
			</div>
49
			<div class="bg-light rounded-3 mb-4 p-5">
50
				<div class="container-fluid py-5">
51
					<h1 class="text-center fw-bold display-5">Bucket List App</h1>
52
					<form class="form-signin" action="/api/validateLogin" method="post">
53
                        <label for="inputEmail" class="sr-only">Email address</label>
54
                        <input type="email" name="inputEmail" id="inputEmail" class="form-control" placeholder="Email address" required autofocus>
55
                        <label for="inputPassword" class="sr-only">Password</label>
56
                        <input type="password" name="inputPassword" id="inputPassword" class="form-control" placeholder="Password" required>
57
58
                        <button id="btnSignIn" class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
59
                    </form>
60
			</div>
61
			        
62
			<footer class="footer">
63
				            
64
				<p>&copy; Company 2022</p>
65
				        
66
			</footer>
67
68
			    
69
		</div>
70
71
	</body>
72
</html>

Open app.py and add a new route for the sign-in interface.

1
@app.route('/signin')
2
def showSignin():
3
    return render_template('signin.html')

Next, open up index.html and signup.html, and add the href link for sign-in on both the pages as /signin. Save all the changes and restart the server.

1
python app.py

Point your browser to http://localhost:5000 and click on the Sign In link, and you should be able to see the sign-in page.

Sign In pageSign In pageSign In page

Implementing Sign-In

Now, we need to create a function to validate the user login. On clicking Sign In, we'll post the entered email address and password to the validate user function.

Creating a Stored Procedure

To validate a user, we'll need a MySQL stored procedure. So create a MySQL stored procedure as shown:

1
DELIMITER $$
2
CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_validateLogin`(
3
IN p_username VARCHAR(20)
4
)
5
BEGIN
6
    select * from tbl_user where user_username = p_username;
7
END$$
8
DELIMITER ;

We'll get the user details based on the username from the MySQL database using sp_validateLogin. Once we have the hashed password, we'll validate it against the password entered by the user.

Validate the User Method

Create a method to validate the user which we'll call when the user submits the form:

1
@app.route('/api/validateLogin',methods=['POST'])
2
def validateLogin():
3
    try:
4
        _username = request.form['inputEmail']
5
        _password = request.form['inputPassword']
6
7
    except Exception as e:
8
        return render_template('error.html',error = str(e))
9

As seen in the above code, we have read the posted email address and password into _username and _password. Now we'll call the sp_validateLogin procedure with the parameter _username. So create a MySQL connection inside the validatelogin method:

1
con = mysql.connect()

Once the connection has been created, create a cursor using the con connection.

1
cursor = con.cursor()

Using the cursor, call the MySQL stored procedure as shown:

1
cursor.callproc('sp_validateLogin',(_username,))

Get the fetched records from the cursor as shown:

1
data = cursor.fetchall()

If the data has some records, we'll match the retrieved password against the password entered by the user.

1
if len(data) > 0:
2
    if check_password_hash(str(data[0][3]),_password):
3
        return redirect('/userhome')
4
    else:
5
        return render_template('error.html',error = 'Wrong Email address or Password.')
6
else:
7
    return render_template('error.html',error = 'Wrong Email address or Password.')

As seen in the above code, we have used a method called check_password_hash to check if the returned hash password matches the password entered by the user. If all is good, then we'll redirect the user to userHome.html. And if there is any error, we'll display error.html with the error message.

Here is the complete validateLogin code:

1
@app.route('/api/validateLogin',methods=['POST'])
2
def validateLogin():
3
    try:
4
        _username = request.form['inputEmail']
5
        _password = request.form['inputPassword']
6
7
8
9
        # connect to mysql

10
11
        con = mysql.connect()
12
        cursor = con.cursor()
13
        cursor.callproc('sp_validateLogin',(_username,))
14
        data = cursor.fetchall()
15
16
17
18
19
        if len(data) > 0:
20
            if check_password_hash(str(data[0][3]),_password):
21
                session['user'] = data[0][0]
22
                return redirect('/userHome')
23
            else:
24
                return render_template('error.html',error = 'Wrong Email address or Password')
25
        else:
26
            return render_template('error.html',error = 'Wrong Email address or Password')
27
28
29
    except Exception as e:
30
        return render_template('error.html',error = str(e))
31
    finally:
32
        cursor.close()
33
        con.close()

Create a page called userhome.html inside the templates folder and add the following HTML code:

1
<!DOCTYPE html>
2
<html lang="en">
3
    <head>
4
		    
5
		<title>Python Flask Bucket List App - Home</title>
6
		    
7
		<link
8
			href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
9
			rel="stylesheet"
10
			integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
11
			crossorigin="anonymous"
12
		/>
13
	</head>
14
15
	<body>
16
		    
17
		<div class="container">
18
			        
19
			<div class="header">
20
				            
21
				<nav
22
					class="border-bottom flex-wrap mb-4 py-3 d-flex justify-content-center"
23
				>
24
					<a
25
						href="/"
26
						class="text-dark text-decoration-none mb-3 mb-md-0 d-flex align-items-center me-md-auto"
27
					>
28
						<span class="fs-4">Python Flask App</span>
29
					</a>
30
31
					<ul class="nav nav-pills">
32
						<li class="nav-item">
33
							<a href="/userhome" class="nav-link">Home</a>
34
						</li>
35
						<li class="nav-item">
36
							<a href="/logout" class="nav-link active">Logout</a>
37
						</li>
38
					</ul>
39
					                        
40
				</nav>
41
				        
42
			</div>
43
			<div class="bg-light rounded-3 mb-4 p-5">
44
				<div class="container-fluid py-5">
45
					<h1 class="text-center fw-bold display-5">Welcome Home!</h1>
46
				</div>
47
			</div>
48
			        
49
			<footer class="footer">
50
				            
51
				<p>&copy; Company 2022</p>
52
				        
53
			</footer>
54
55
			    
56
		</div>
57
	</body>
58
</html>

Also, create an error page called error.html in the templates folder and add the following HTML code:

1
<!DOCTYPE html>
2
<html lang="en">
3
    <head>
4
		    
5
		<title>Error - Python Flask App</title>
6
		    
7
		<link
8
			href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
9
			rel="stylesheet"
10
			integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
11
			crossorigin="anonymous"
12
		/>
13
	</head>
14
15
	<body>
16
		    
17
		<div class="container">
18
			        
19
			<div class="header">
20
				            
21
				<nav
22
					class="border-bottom flex-wrap mb-4 py-3 d-flex justify-content-center"
23
				>
24
					<a
25
						href="/"
26
						class="text-dark text-decoration-none mb-3 mb-md-0 d-flex align-items-center me-md-auto"
27
					>
28
						<span class="fs-4">Python Flask App</span>
29
					</a>
30
31
					<ul class="nav nav-pills">
32
						<li class="nav-item">
33
							<a href="/" class="nav-link">Home</a>
34
						</li>
35
						<li class="nav-item">
36
							<a href="/signup" class="nav-link">Signup</a>
37
						</li>
38
						<li class="nav-item">
39
							<a href="/signin" class="nav-link">Sign In</a>
40
						</li>
41
					</ul>
42
					                        
43
				</nav>
44
				        
45
			</div>
46
			<div class="bg-light rounded-3 mb-4 p-5">
47
				<div class="container-fluid py-5">
48
					<h1 class="text-center fw-bold display-5">{{error}}</h1>
49
				</div>
50
			</div>
51
			        
52
			<footer class="footer">
53
				            
54
				<p>&copy; Company 2022</p>
55
				        
56
			</footer>
57
58
			    
59
		</div>
60
	</body>
61
</html>

Inside error.html, we have an element as shown:

1
<h1 class="text-center fw-bold display-5">{{error}}</h1>

The value for the variable can be passed from the render_template function and can be set dynamically.

On successful sign-in, we are redirecting the user to the user home page, so we need to create a route called /userHome as shown:

1
@app.route('/userHome')
2
def userHome():
3
    return render_template('userHome.html')
4
    

Save all the changes and restart the server. Click on the Sign In link on the home page and try to sign in using a valid email address and password. On successful user validation, you should have a page as shown below:

Home screenHome screenHome screen

On an unsuccessful user validation, the user will be redirected to an error page as shown below:

Error screenError screenError screen

Here we have used a separate error page to display the error. It's also fine if you want to use the same page to display the error message.

Restricting Unauthorized Access to the User Home Page

On successful user validation, a user is redirected to the user home page. But right now even an unauthorized user can view the home page by simply browsing the URL http://localhost:5000/userhome.

To restrict unauthorized user access, we'll check for a session variable which we'll set on successful user login. So import session from flask:

1
from flask import session

We also need to set a secret key for the session. So in app.py, after the app has been initialized, set the secret key as shown :

1
app.secret_key = 'why would I tell you my secret key?'

Now, inside the validateLogin method, before redirecting the user to /userhome on successful sign-in, set the session variable as shown:

1
session['user'] = data[0][0]

Next, inside the userhome method, check for the session variable before rendering userhome.html. If the session variable is not found, redirect to the error page.

1
@app.route('/userhome')
2
def userHome():
3
    if session.get('user'):
4
        return render_template('userhome.html')
5
    else:
6
        return render_template('error.html',error = 'Unauthorized Access')

Save all the changes and restart the server. Without signing in, try to navigate to http://localhost:5000/userhome and since you haven't logged in yet, you should be redirected to the error page.

Unauthorized access errorUnauthorized access errorUnauthorized access error

Implementing Logout

Implementing the logout functionality is the simplest. All we need to do is make the session variable user null and redirect the user to the main page.

Inside app.py, create a new route and method for logout as shown:

1
@app.route('/logout')
2
def logout():
3
    session.pop('user',None)
4
    return redirect('/')

We have already set the href for the log out button to /logout. So save all the changes and restart the server. From the home page, click on Sign In and try to log in using a valid email address and password. Once you've signed in, click on the Logout button in user home and you should be successfully logged out from the application.

Conclusion

In this part of the tutorial, we saw how to implement the user login and logout functionality. We also saw how to restrict unauthorized access to application pages. In the next part of this tutorial, we'll implement the functionality for the logged-in user to add and edit a blog post in the application.

This post has been updated with contributions from Jacob Jackson. Jacob is a web developer, technical writer, freelancer, and open-source contributor.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.