DEV Community

Cover image for Building a Full Stack Web Application using Flask (Python Web Framework) - Part One
Stephen Omoregie
Stephen Omoregie

Posted on • Updated on

Building a Full Stack Web Application using Flask (Python Web Framework) - Part One

Python is pretty much a jack-of-all-trades programming language, with a simple syntax that makes it easily understood even for beginners. So, using its web framework (Flask) for building web applications is just as exciting and has an easier learning curve.

As a Fullstack Web developer, you will be responsible for handling both the front-end and back-end aspects of any project you're working on. This includes building user interfaces (templates), back-end logic, data modelling/persisting data in a database, third-party API integrations, or even building your own API services for others to consume.

Image Funny Emoji

Introduction

In this comprehensive guide, we'll be looking at how Flask helps simplify this seemingly complex skill set that is required for being a full-stack developer. We'll touch on topics like project structure, dependencies/third-party packages, Blueprints for writing modular code, file handling, database connection using MongoDB (PyMongo), User Registration/Authentication and Authorization.

Let's begin! ๐Ÿ˜Ž

Setting Up the Development Environment

I'll assume you already have python installed on your machine; I mean, if not, why not! (Download Here)

  • Installing Flask and Dependencies It's always advisable to run your project using virtual environments (example, the venv module), this will help isolate project dependencies specific to your project.

First of all, create a directory/folder for your project, let's call it Tutorial.

# Step 1: Create a new virtual environment
$ cd Tutorial/
$ python3 -m venv myenv

# Step 2: Activate the virtual environment
$ source myenv/bin/activate
Enter fullscreen mode Exit fullscreen mode

Note: For terminal based commands, we'll stick with the linux version now, though it'll also be similar for Windows OS.

Now with the virtual environment activated, let's install some dependencies we'll be needing for the project.

(myvenv)Tutorial$ pip3 install flask pymongo flask-login
Enter fullscreen mode Exit fullscreen mode

Project Structure

While Flask is not so strict on how you MUST write your code and structure your project, it helps to have an organized structure for better code maintainability and scalability. For this project, here's a structure that I'll recommend we follow:

Tutorial/
โ”œโ”€โ”€ app.py
โ””โ”€โ”€ blueprints/
โ”‚   โ””โ”€โ”€ main_blueprint.py
โ”œโ”€โ”€ models/
โ”‚   โ””โ”€โ”€ models.py
โ”œโ”€โ”€ myvenv/
โ”œโ”€โ”€ static/
โ”‚   โ””โ”€โ”€ css/
โ”‚   โ””โ”€โ”€ js/
โ”‚   โ””โ”€โ”€ images/
โ”œโ”€โ”€ templates/
โ”‚   โ””โ”€โ”€ index.html
โ”‚   โ””โ”€โ”€ base.html
โ”œโ”€โ”€ requirements.txt


Enter fullscreen mode Exit fullscreen mode
  • app.py: This file will be at the root of our project directory. This file will serve as the entry point of our application. We'll instantiate the flask app in this file and other configurations.

  • templates: This directory will be where we store our html files that will be rendered when a page route is requested. Flask comes with built-in support for Jinja templating system which allows us to build pages with dynamic contents.

  • static: This directory will house the static assets needed by the application, such as css, images, javascript files. These assets will be served as needed.

  • blueprints: To make sure our app.py file is not crowded with many routes and other configurations, we'll be separating our routes into different files and using Blueprints will help us create routes in a separate file/directory and register them on our app.

  • models: In this directory, we'll create the model for our user for authentication. Or any type of model you need for your specific use case. In the file models.py, we'll be connecting to our database as well as creating a class needed by Flask-login to help with Authentication / Authorization on our app.

Running a Test Server

Flask has a built-in development server for easy testing and debugging of your application. Before we start the server, create a file app.py at the root of your project directory with the content below:

# File: Tutorial/app.py
from flask import Flask

# create an instance of the flask application
app = Flask(__name__)

# Create a route on your app
@app.route("/", strict_slashes=False, methods=["GET"])
def index():
    return "<h1>This is the Home Page</h1>"
Enter fullscreen mode Exit fullscreen mode

A little bit about what's going on up there

At first, we import the Flask class from the flask package. Then we create an instance of our application and save it to the variable app, we also pass a special variable __name__ to the Flask class initialization. The __name__ argument is used to determine the root path of the application so it can find resource files relative to the location of the script. This is necessary for Flask to know where to look for resources such as templates and static files.

Next, we create a route decorator with a rule "/" that matches when our site is visited on www.example.com/ or www.example.com (the index page), we specify strict_slashes=False, this is to allow a more flexible url such that /about/ and /about will be treated as the same. We also specify the HTTP Request method that the route will respond to; the methods variable receives an array of string(s) (HTTP Methods like "GET", "POST", "PUT", "DELETE").

Just under the route decorator, a function is defined index which will handle the logic that determines what will be returned when the index url is requested. This can be something as simple as an HTML string (as in our example), or you can return JSON data or render a template to view.

Running the Development Server

# At the root of your project run the code
# below to start your development server
(myvenv) Tutorial$ flask run --port 5100 --debug
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5100
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 139-345-994

Enter fullscreen mode Exit fullscreen mode
(myvenv) Tutorial$ flask run --port 5100 --debug
Enter fullscreen mode Exit fullscreen mode

With the entry point of our application being app.py, the above command will run our application on port localhost:5100 with debug mode activate. Debug mode ensures that our application restarts whenever we make changes to the files. Meaning you don't have to kill the server and relaunch again whenever you make a change.

Testing out our web server from the terminal.

(myvenv) Tutorial$ curl localhost:5100; echo "";
<h1>This is the Home Page</h1>
(myvenv) Tutorial$ 
Enter fullscreen mode Exit fullscreen mode

Yaay! You just took the first step in launching a functional web server using flask. You can also visit the url localhost:5100 and your server will be there to answer!

Writing Modular Code with Blueprints

Blueprints are a fundamental concept in Flask especially for structuring larger applications. They are helpful in organizing your codebase by grouping related views, templates and static files into reusable modules. This promotes maintainability, readability and separation of concerns.

We'll create two blueprints for this project. In the file main_blueprint.py and auth_blueprint.py and subsequently register the blueprints on the app instance in app.py.

# File: blueprint/main_blueprint.py
from flask import Blueprint, request

main_views = Blueprint("main", __name__)

# Create routes on this blueprint instance
@main_views.get("/", strict_slashes=False)
def index():
    # Define application logic for homepage
    return "<h1>This is the Home Page</h1>"


@main_views.get("/profile/<string:username>", strict_slashes=False)
def profile(username):
    # Define application logic for profile page
    return f"<h1>Welcome {username}! This is your profile</h1>"

Enter fullscreen mode Exit fullscreen mode
# File: blueprint/auth_blueprint.py
from flask import Blueprint, request

auth_views = Blueprint("auth", __name__)

# Create routes on this blueprint instance
@auth_views.route("/register", strict_slashes=False, methods=["GET", "POST"])
def register():
    # Define application logic for homepage
    if request.method == "POST":
        # Enter logic for processing registration
        return "<h1>After Registration</h1>"

    return "<h1>This is the Register Page</h1>"


@auth_views.route("/login", strict_slashes=False, methods=["GET", "POST"])
def login():
    # Define application logic for profile page
    if request.method == "POST":
        # Enter logic for processing login
        return "<h1>After Login</h1>"

    return "<h1>Here goes the Login Page</h1>"

Enter fullscreen mode Exit fullscreen mode

Now we have to register these blueprints on the app. Let's modify the app.py file.

from flask import Flask
from blueprints.main_blueprint import main_views
from blueprints.auth_blueprint import auth_views

# create an instance of the flask application
app = Flask(__name__)

app.register_blueprint(main_views)
app.register_blueprint(auth_views)

Enter fullscreen mode Exit fullscreen mode

With the code part covered, let's try to understand what's going on in the code snippets.

  • auth_views = Blueprint("auth", __name__): Here we create an instance of the Blueprint class and store it in auth_views variable. The first parameter is the name of the blueprint, this will be used to reference the functions defined for various routes with the url_for() function. Example url_for("auth.login"), we also pass the __name__ variable to register the location of the specific blueprint.

  • @auth_views.route('/register', methods=["GET", "POST"]): When defining routes, you can choose to specifically use the .get or .post or .put or .delete to restrict a function to respond to a specific HTTP request method. But using the .route method on the view, you can specify an array of methods you want the function to respond to, just like we've done with the /login and /register routes.

  • from flask import Blueprint, request: Whether you're within a blueprint or app instance file, you would need access to the request module, to allow you get access to the request object coming in from the user accessing your application. You can use dir(request) within one of your route functions to see what you have access to, eg. the method, the url params, the form, files, etc. More on this later.

So far, we've been sending simple HTML Strings directly from our views, next step, we'll talk about rendering static html template with or without dynamic data.

Working with Templates (Jinja)

While it may be tempting to just write the html as strings in your function, I mean, who doesn't like the lazy way out.
Image Dancing Mr Bean

No!! You will not do that! Not on my watch! Haha.
Templates offer us many great tools to create the visible part of our web application which the users will be interacting with. Our templates will be good ol' html code, but extended with the Jinja templating engine, which will allow us do things like, extending an html (as base), dynamically loading assets according to page, variables, creating DOM elements with loops, conditional rendering of elements, etc.

Here's an example of home page for our project. To focus more on the learning of templating system, we'll put part of the base.html and index.html here. You can find the source code for this project on GitHub @ Flask_App_Tutorial

The base.html template will serve as a layout that all other child templates will inherit/extend. More on this later.

<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flask App</title>
    <link rel="stylesheet" href="{{url_for('static', filename='css/base.css')}}">
    <script src="{{url_for('static', filename='js/base.js')}}" defer></script>
    {% block css %}{% endblock css %}
    {% block js %}{% endblock js %}
</head>
<body>
    <header class="header-container">
        <a href="/"><h2 class="header-title">Flask Project</h2></a>
        <nav class="navigation-container">
            <ul>
                <a href="/login"><li class="link">Sign In</li></a>
                <a href="/register"><li class="link">Sign Up</li></a>
                <a href="/profile"><li class="link">Profile</li></a>
                <a href="/logout"><li class="link">Sign Out</li></a>
            </ul>
        </nav>
    </header>
    <!--Here other html files that extend this base template will come here 
    provided the names match, as with this example body_content-->
    {% block body_content %}{% endblock body_content %}

    <!--You can create multiple block contents that will be rendered when the 
    template that extends this template with matching block name will 
    rendered in the block place-->

    <footer class="footer-container">
        <p class="footer-details">Happy Coding! Feel free to use this project as a boilerplate for your other projects</p>
    </footer>
</body>
</html>

Enter fullscreen mode Exit fullscreen mode
<!-- templates/index.html -->
{% extends 'base.html' %}

{% block css %}
<link rel="stylesheet" href="{{url_for("static", filename="css/index.css")}}">
{% endblock css %}

{% block js %}
<script src="{{url_for("static", filename="js/index.js")}}" defer></script>
{% endblock js %}

{% block body_content %}
<main class="main-container">
    <h1 class="main-header">Welcome to the Home Page!</h1>
    <p class="main-subtitle">Our little project is for learning building apps with Flask!</p>

</main>
{% endblock body_content %}
Enter fullscreen mode Exit fullscreen mode

Now, let's wrap our head around what's happening in the above, it must look very different from the normal HTML you know, but let's touch some of the points.

Sure, here's a quick explanation of the templating syntax used in our Flask application:

  1. {{ ... }}: This is used to insert the value of a Python expression into the HTML. For example, {{ url_for('static', filename='css/base.css') }} calls the url_for function with the arguments 'static' and filename='css/base.css', and inserts the result into the HTML. It is also used for inserting the values of a variable dynamically into your html file(s).

  2. {% ... %}: This is used to execute Python statements. For example, {% block css %}{% endblock css %} defines a block that can be filled in by a child template. The block statement is a control statement in the Jinja template engine (which Flask uses). It defines a block that child templates can override. For conditional blocks, you have something like {%if variable == "value" %} Do this or that {% endif %}

  3. {% block ... %}{% endblock ... %}: This defines a block in the template. Blocks are sections that child templates can override. In your template, you have defined two blocks: css and js for additional CSS and JavaScript files respectively, and body_content for the main content of the page.

  4. {{ url_for('static', filename='...') }}: This is a Flask function that generates a URL for a static file. Flask automatically serves static files from the /static/ directory in your application's root directory. The url_for function generates the correct URL to these files, regardless of how your application is deployed.

This is a Jinja template that extends base.html. Here's a breakdown of the syntax:

  1. {% extends 'base.html' %}: This line tells Jinja that this template "extends" base.html. That means it starts with the content of base.html, and then fills in or replaces blocks defined in base.html.

  2. {% block css %}...{% endblock css %}: This block is used to add additional CSS files specific to this page. The link tag within this block is used to link the index.css file from the static directory.

  3. {% block js %}...{% endblock js %}: Similar to the CSS block, this block is used to add JavaScript files specific to this page. The script tag within this block is used to link the index.js file from the static directory.

  4. {% block body_content %}...{% endblock body_content %}: This block is used to define the main content of the page. The HTML within this block will replace the body_content block in base.html.

Try your hands on this; practice makes great! More resources on this at the end of this guide.

Let's call it a wraps this time, we'll continue with the remaining parts of this guide where we'll learn about:

  • Handling Form Submissions/File Uploads
  • Downloading Files
  • Connecting with a Database for Data Insertion and Retrieval using MongoDB
  • User Authentication and Authorization with Flask-Login

Resources:

  1. Source Code: https://github.com/Cre8steveDev/Flask_App_Tutorial
  2. Flask Documentation: https://flask.palletsprojects.com/en/3.0.x/
  3. Jinja Templating Documentation: https://jinja.palletsprojects.com/en/3.1.x/
  4. PART TWO PUBLISHED HERE

Top comments (13)

Collapse
 
cre8stevedev profile image
Stephen Omoregie

Feel free to leave your questions or kind comments!

Image Dancerrrr

Collapse
 
taijidude profile image
taijidude

That ist such a great GIF.

Collapse
 
cre8stevedev profile image
Stephen Omoregie

๐Ÿ˜ I agree with you. The minions own got me smiling everytime

Collapse
 
gadrawingz profile image
Gad Iradufasha

The code part was awesome as this guy dancing!

Collapse
 
cre8stevedev profile image
Stephen Omoregie

Haha... Thank you man. Happy coding

Collapse
 
sarma_akondi_746f338b83b7 profile image
Sarma Akondi

Nice tutorial ๐Ÿ™Œ

By the way, your portfolio website is awesome ๐Ÿคฉ

Just a small typo I observed - it says โ€œPython (Flash and Django)โ€, I believe itโ€™s supposed to be Flask ๐Ÿ‘

Collapse
 
cre8stevedev profile image
Stephen Omoregie

Hey Man, thanks for your kind words! ๐Ÿ˜Œ
Wow! Great observation about the typo on my portfolio, haha, "Flash". I've made the correction on it. Thanks once again

Image running

Collapse
 
sarma_akondi_746f338b83b7 profile image
Sarma Akondi

๐Ÿ˜‚ that gif is funny and disturbing and creepy at the same time

Collapse
 
kasambalaji profile image
KasamBalaji

Waiting for next part!

Collapse
 
cre8stevedev profile image
Stephen Omoregie

Hello Kasam! Thanks for reading part one, here's the link to the concluding part of the guide. Cheers man.

dev.to/cre8stevedev/building-a-ful...

Collapse
 
cre8stevedev profile image
Stephen Omoregie

๐Ÿ˜ Thanks man! It'll be ready and deployed here soonest

Collapse
 
stokry profile image
Stokry

Django is much more robust for larger applications!

Collapse
 
cre8stevedev profile image
Stephen Omoregie

Yes! But Flask is more beginner friendly and is a good start for anyone trying to build simple web stuffs in python before getting into the battery-included complexity-ish ease Django offers