How to create a website using python flask

The author selected the Free and Open Source Fund to receive a donation as part of the Write for DOnations program.

Introduction

Flask is a small and lightweight Python web framework that provides useful tools and features that make creating web applications in Python easier. It gives developers flexibility and is a more accessible framework for new developers since you can build a web application quickly using only a single Python file. Flask is also extensible and doesn’t force a particular directory structure or require complicated boilerplate code before getting started.

As part of this tutorial, you’ll use the Bootstrap toolkit to style your application so it is more visually appealing. Bootstrap will help you incorporate responsive web pages in your web application so that it also works well on mobile browsers without writing your own HTML, CSS, and JavaScript code to achieve these goals. The toolkit will allow you to focus on learning how Flask works.

Flask uses the Jinja template engine to dynamically build HTML pages using familiar Python concepts such as variables, loops, lists, and so on. You’ll use these templates as part of this project.

In this tutorial, you’ll build a small web blog using Flask and SQLite in Python 3. Users of the application can view all the posts in your database and click on the title of a post to view its contents with the ability to add a new post to the database and edit or delete an existing post.

Prerequisites

Before you start following this guide, you will need:

  • A local Python 3 programming environment, follow the tutorial for your distribution in How To Install and Set Up a Local Programming Environment for Python 3 series for your local machine. In this tutorial we’ll call our project directory flask_blog.
  • An understanding of Python 3 concepts, such as data types, conditional statements, for loops, functions, and other such concepts. If you are not familiar with Python, check out our How To Code in Python 3 series.

Step 1 — Installing Flask

In this step, you’ll activate your Python environment and install Flask using the pip package installer.

If you haven’t already activated your programming environment, make sure you’re in your project directory (flask_blog) and use the following command to activate the environment:

  1. source env/bin/activate

Once your programming environment is activated, your prompt will now have an env prefix that may look as follows:

This prefix is an indication that the environment env is currently active, which might have another name depending on how you named it during creation.

Note: You can use Git, a version control system, to effectively manage and track the development process for your project. To learn how to use Git, you might want to check out our Introduction to Git Installation Usage and Branches article.

If you are using Git, it is a good idea to ignore the newly created env directory in your .gitignore file to avoid tracking files not related to the project.

Now you’ll install Python packages and isolate your project code away from the main Python system installation. You’ll do this using pip and python.

To install Flask, run the following command:

  1. pip install flask

Once the installation is complete, run the following command to confirm the installation:

  1. python -c "import flask; print(flask.__version__)"

You use the python command line interface with the option -c to execute Python code. Next you import the flask package with import flask; then print the Flask version, which is provided via the flask.__version__ variable.

The output will be a version number similar to the following:

Output

1.1.2

You’ve created the project folder, a virtual environment, and installed Flask. You’re now ready to move on to setting up your base application.

Step 2 — Creating a Base Application

Now that you have your programming environment set up, you’ll start using Flask. In this step, you’ll make a small web application inside a Python file and run it to start the server, which will display some information on the browser.

In your flask_blog directory, open a file named hello.py for editing, use nano or your favorite text editor:

  1. nano hello.py

This hello.py file will serve as a minimal example of how to handle HTTP requests. Inside it, you’ll import the Flask object, and create a function that returns an HTTP response. Write the following code inside hello.py:

flask_blog/hello.py

from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello():
    return 'Hello, World!'

In the preceding code block, you first import the Flask object from the flask package. You then use it to create your Flask application instance with the name app. You pass the special variable __name__ that holds the name of the current Python module. It’s used to tell the instance where it’s located—you need this because Flask sets up some paths behind the scenes.

Once you create the app instance, you use it to handle incoming web requests and send responses to the user. @app.route is a decorator that turns a regular Python function into a Flask view function, which converts the function’s return value into an HTTP response to be displayed by an HTTP client, such as a web browser. You pass the value '/' to @app.route() to signify that this function will respond to web requests for the URL /, which is the main URL.

The hello() view function returns the string 'Hello, World!' as a response.

Save and close the file.

To run your web application, you’ll first tell Flask where to find the application (the hello.py file in your case) with the FLASK_APP environment variable:

  1. export FLASK_APP=hello

Then run it in development mode with the FLASK_ENV environment variable:

  1. export FLASK_ENV=development

Lastly, run the application using the flask run command:

  1. flask run

Once the application is running the output will be something like this:

Output

* Serving Flask app "hello" (lazy loading) * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 813-894-335

The preceding output has several pieces of information, such as:

  • The name of the application you’re running.
  • The environment in which the application is being run.
  • Debug mode: on signifies that the Flask debugger is running. This is useful when developing because it gives us detailed error messages when things go wrong, which makes troubleshooting easier.
  • The application is running locally on the URL http://127.0.0.1:5000/, 127.0.0.1 is the IP that represents your machine’s localhost and :5000 is the port number.

Open a browser and type in the URL http://127.0.0.1:5000/, you will receive the string Hello, World! as a response, this confirms that your application is successfully running.

Warning Flask uses a simple web server to serve our application in a development environment, which also means that the Flask debugger is running to make catching errors easier. This development server should not be used in a production deployment. See the Deployment Options page on the Flask documentation for more information, you can also check out this Flask deployment tutorial.

You can now leave the development server running in the terminal and open another terminal window. Move into the project folder where hello.py is located, activate the virtual environment, set the environment variables FLASK_ENV and FLASK_APP, and continue to the next steps. (These commands are listed earlier in this step.)

Note: When opening a new terminal, it is important to remember activating the virtual environment and setting the environment variables FLASK_ENV and FLASK_APP.

While a Flask application’s development server is already running, it is not possible to run another Flask application with the same flask run command. This is because flask run uses the port number 5000 by default, and once it is taken, it becomes unavailable to run another application on so you would receive an error similar to the following:

Output

OSError: [Errno 98] Address already in use

To solve this problem, either stop the server that’s currently running via CTRL+C, then run flask run again, or if you want to run both at the same time, you can pass a different port number to the -p argument, for example, to run another application on port 5001 use the following command:

  1. flask run -p 5001

You now have a small Flask web application. You’ve run your application and displayed information on the web browser. Next, you’ll use HTML files in your application.

Step 3 — Using HTML templates

Currently your application only displays a simple message without any HTML. Web applications mainly use HTML to display information for the visitor, so you’ll now work on incorporating HTML files in your app, which can be displayed on the web browser.

Flask provides a render_template() helper function that allows use of the Jinja template engine. This will make managing HTML much easier by writing your HTML code in .html files as well as using logic in your HTML code. You’ll use these HTML files, (templates) to build all of your application pages, such as the main page where you’ll display the current blog posts, the page of the blog post, the page where the user can add a new post, and so on.

In this step, you’ll create your main Flask application in a new file.

First, in your flask_blog directory, use nano or your favorite editor to create and edit your app.py file. This will hold all the code you’ll use to create the blogging application:

  1. nano app.py

In this new file, you’ll import the Flask object to create a Flask application instance as you previously did. You’ll also import the render_template() helper function that lets you render HTML template files that exist in the templates folder you’re about to create. The file will have a single view function that will be responsible for handling requests to the main / route. Add the following content:

flask_blog/app.py

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

The index() view function returns the result of calling render_template() with index.html as an argument, this tells render_template() to look for a file called index.html in the templates folder. Both the folder and the file do not yet exist, you will get an error if you were to run the application at this point. You’ll run it nonetheless so you’re familiar with this commonly encountered exception. You’ll then fix it by creating the needed folder and file.

Save and exit the file.

Stop the development server in your other terminal that runs the hello application with CTRL+C.

Before you run the application, make sure you correctly specify the value for the FLASK_APP environment variable, since you’re no longer using the application hello:

  1. export FLASK_APP=app
  2. flask run

Opening the URL http://127.0.0.1:5000/ in your browser will result in the debugger page informing you that the index.html template was not found. The main line in the code that was responsible for this error will be highlighted. In this case, it is the line return render_template('index.html').

If you click this line, the debugger will reveal more code so that you have more context to help you solve the problem.

How to create a website using python flask

To fix this error, create a directory called templates inside your flask_blog directory. Then inside it, open a file called index.html for editing:

  1. mkdir templates
  2. nano templates/index.html

Next, add the following HTML code inside index.html:

flask_blog/templates/index.html

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>FlaskBlogtitle>
head>
<body>
   <h2>Welcome to FlaskBlogh2>
body>
html>

Save the file and use your browser to navigate to http://127.0.0.1:5000/ again, or refresh the page. This time the browser should display the text Welcome to FlaskBlog in an

tag.

In addition to the templates folder, Flask web applications also typically have a static folder for hosting static files, such as CSS files, JavaScript files, and images the application uses.

You can create a style.css style sheet file to add CSS to your application. First, create a directory called static inside your main flask_blog directory:

  1. mkdir static

Then create another directory called css inside the static directory to host .css files. This is typically done to organize static files in dedicated folders, as such, JavaScript files typically live inside a directory called js, images are put in a directory called images (or img), and so on. The following command will create the css directory inside the static directory:

  1. mkdir static/css

Then open a style.css file inside the css directory for editing:

  1. nano static/css/style.css

Add the following CSS rule to your style.css file:

flask_blog/static/css/style.css

h2 {
    border: 2px #eee solid;
    color: brown;
    text-align: center;
    padding: 10px;
}

The CSS code will add a border, change the color to brown, center the text, and add a little padding to

tags.

Save and close the file.

Next, open the index.html template file for editing:

  1. nano templates/index.html

You’ll add a link to the style.css file inside the section of the index.html template file:

flask_blog/templates/index.html

. . .
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="{{ url_for('static', filename= 'css/style.css') }}">
    <title>FlaskBlogtitle>
head>
. . .

Here you use the url_for() helper function to generate the appropriate location of the file. The first argument specifies that you’re linking to a static file and the second argument is the path of the file inside the static directory.

Save and close the file.

Upon refreshing the index page of your application, you will notice that the text Welcome to FlaskBlog is now in brown, centered, and enclosed inside a border.

You can use the CSS language to style the application and make it more appealing using your own design. However, if you’re not a web designer, or if you aren’t familiar with CSS, then you can use the Bootstrap toolkit, which provides easy-to-use components for styling your application. In this project, we’ll use Bootstrap.

You might have guessed that making another HTML template would mean repeating most of the HTML code you already wrote in the index.html template. You can avoid unnecessary code repetition with the help of a base template file, which all of your HTML files will inherit from. See Template Inheritance in Jinja for more information.

To make a base template, first create a file called base.html inside your templates directory:

  1. nano templates/base.html

Type the following code in your base.html template:

flask_blog/templates/base.html

doctype html>
<html lang="en">
  <head>
    
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

    <title>{% block title %} {% endblock %}title>
  head>
  <body>
    <nav class="navbar navbar-expand-md navbar-light bg-light">
        <a class="navbar-brand" href="{{ url_for('index')}}">FlaskBloga>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon">span>
        button>
        <div class="collapse navbar-collapse" id="navbarNav">
            <ul class="navbar-nav">
            <li class="nav-item active">
                <a class="nav-link" href="#">Abouta>
            li>
            ul>
        div>
    nav>
    <div class="container">
        {% block content %} {% endblock %}
    div>

    
    
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous">script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous">script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous">script>
  body>
html>

Save and close the file once you’re done editing it.

Most of the code in the preceding block is standard HTML and code required for Bootstrap. The tags provide information for the web browser, the tag links the Bootstrap CSS files, and the

flask_blog/app.py

. . .

@app.route('/')
def index():
    conn = get_db_connection()
    posts = conn.execute('SELECT * FROM posts').fetchall()
    conn.close()
    return render_template('index.html', posts=posts)

In this new version of the index() function, you first open a database connection using the get_db_connection() function you defined earlier. Then you execute an SQL query to select all entries from the posts table. You implement the fetchall() method to fetch all the rows of the query result, this will return a list of the posts you inserted into the database in the previous step.

You close the database connection using the close() method and return the result of rendering the index.html template. You also pass the posts object as an argument, which contains the results you got from the database, this will allow you to access the blog posts in the index.html template.

With these modifications in place, save and close the app.py file.

Now that you’ve passed the posts you fetched from the database to the index.html template, you can use a for loop to display each post on your index page.

Open the index.html file:

  1. nano templates/index.html

Then, modify it to look as follows:

flask_blog/templates/index.html

{% extends 'base.html' %}

{% block content %}
    <h2>{% block title %} Welcome to FlaskBlog {% endblock %}h2>
    {% for post in posts %}
        <a href="#">
            <h2>{{ post['title'] }}h2>
        a>
        <span class="badge badge-primary">{{ post['created'] }}span>
        <hr>
    {% endfor %}
{% endblock %}

Here, the syntax {% for post in posts %} is a Jinja for loop, which is similar to a Python for loop except that it has to be later closed with the {% endfor %} syntax. You use this syntax to loop over each item in the posts list that was passed by the index() function in the line return render_template('index.html', posts=posts). Inside this for loop, you display the post title in an

heading inside an tag (you’ll later use this tag to link to each post individually).

You display the title using a literal variable delimiter ({{ ... }}). Remember that post will be a dictionary-like object, so you can access the post title with post['title']. You also display the post creation date using the same method.

Once you are done editing the file, save and close it. Then navigate to the index page in your browser. You’ll see the two posts you added to the database on your page.

How to create a website using python flask

Now that you’ve modified the index() view function to display all the posts you have in the database on your application’s homepage, you’ll move on to display each post in a single page and allow users to link to each individual post.

Step 6 — Displaying a Single Post

In this step, you’ll create a new Flask route with a view function and a new HTML template to display an individual blog post by its ID.

By the end of this step, the URL http://127.0.0.1:5000/1 will be a page that displays the first post (because it has the ID 1). The http://127.0.0.1:5000/ID URL will display the post with the associated ID number if it exists.

Open app.py for editing:

  1. nano app.py

Since you’ll need to get a blog post by its ID from the database in multiple locations later in this project, you’ll create a standalone function called get_post(). You can call it by passing it an ID and receive back the blog post associated with the provided ID, or make Flask respond with a 404 Not Found message if the blog post does not exist.

To respond with a 404 page, you need to import the abort() function from the Werkzeug library, which was installed along with Flask, at the top of the file:

flask_blog/app.py

import sqlite3
from flask import Flask, render_template
from werkzeug.exceptions import abort

. . .

Then, add the get_post() function right after the get_db_connection() function you created in the previous step:

flask_blog/app.py

. . .

def get_db_connection():
    conn = sqlite3.connect('database.db')
    conn.row_factory = sqlite3.Row
    return conn


def get_post(post_id):
    conn = get_db_connection()
    post = conn.execute('SELECT * FROM posts WHERE id = ?',
                        (post_id,)).fetchone()
    conn.close()
    if post is None:
        abort(404)
    return post

. . .

This new function has a post_id argument that determines what blog post to return.

Inside the function, you use the get_db_connection() function to open a database connection and execute a SQL query to get the blog post associated with the given post_id value. You add the fetchone() method to get the result and store it in the post variable then close the connection. If the post variable has the value None, meaning no result was found in the database, you use the abort() function you imported earlier to respond with a 404 error code and the function will finish execution. If however, a post was found, you return the value of the post variable.

Next, add the following view function at the end of the app.py file:

flask_blog/app.py

. . .

@app.route('/')
def post(post_id):
    post = get_post(post_id)
    return render_template('post.html', post=post)

In this new view function, you add a variable rule to specify that the part after the slash (/) is a positive integer (marked with the int converter) that you need to access in your view function. Flask recognizes this and passes its value to the post_id keyword argument of your post() view function. You then use the get_post() function to get the blog post associated with the specified ID and store the result in the post variable, which you pass to a post.html template that you’ll soon create.

Save the app.py file and open a new post.html template file for editing:

  1. nano templates/post.html

Type the following code in this new post.html file. This will be similar to the index.html file, except that it will only display a single post, in addition to also displaying the contents of the post:

flask_blog/templates/post.html

{% extends 'base.html' %}

{% block content %}
    <h2>{% block title %} {{ post['title'] }} {% endblock %}h2>
    <span class="badge badge-primary">{{ post['created'] }}span>
    <p>{{ post['content'] }}p>
{% endblock %}

You add the title block that you defined in the base.html template to make the title of the page reflect the post title that is displayed in an

heading at the same time.

Save and close the file.

You can now navigate to the following URLs to see the two posts you have in your database, along with a page that tells the user that the requested blog post was not found (since there is no post with an ID number of 3 so far):

http://127.0.0.1:5000/1
http://127.0.0.1:5000/2
http://127.0.0.1:5000/3

Going back to the index page, you’ll make each post title link to its respective page. You’ll do this using the url_for() function. First, open the index.html template for editing:

  1. nano templates/index.html

Then change the value of the href attribute from # to {{ url_for('post', post_id=post['id']) }} so that the for loop will look exactly as follows:

flask_blog/templates/index.html

{% for post in posts %}
    <a href="{{ url_for('post', post_id=post['id']) }}">
        <h2>{{ post['title'] }}h2>
    a>
    <span class="badge badge-primary">{{ post['created'] }}span>
    <hr>
{% endfor %}

Here, you pass 'post' to the url_for() function as a first argument. This is the name of the post() view function and since it accepts a post_id argument, you give it the value post['id']. The url_for() function will return the proper URL for each post based on its ID.

Save and close the file.

The links on the index page will now function as expected. With this, you’ve now finished building the part of the application responsible for displaying the blog posts in your database. Next, you’ll add the ability to create, edit, and delete blog posts to your application.

Step 7 — Modifying Posts

Now that you’ve finished displaying the blog posts that are present in the database on the web application, you need to allow the users of your application to write new blog posts and add them to the database, edit the existing ones, and delete unnecessary blog posts.

Creating a New Post

Up to this point, you have an application that displays the posts in your database but provides no way of adding a new post unless you directly connect to the SQLite database and add one manually. In this section, you’ll create a page on which you will be able to create a post by providing its title and content.

Open the app.py file for editing:

  1. nano app.py

First, you’ll import the following from the Flask framework:

  • The global request object to access incoming request data that will be submitted via an HTML form.
  • The url_for() function to generate URLs.
  • The flash() function to flash a message when a request is processed.
  • The redirect() function to redirect the client to a different location.

Add the imports to your file like the following:

flask_blog/app.py

import sqlite3
from flask import Flask, render_template, request, url_for, flash, redirect
from werkzeug.exceptions import abort

. . .

The flash() function stores flashed messages in the client’s browser session, which requires setting a secret key. This secret key is used to secure sessions, which allow Flask to remember information from one request to another, such as moving from the new post page to the index page. The user can access the information stored in the session, but cannot modify it unless they have the secret key, so you must never allow anyone to access your secret key. See the Flask documentation for sessions for more information.

To set a secret key, you’ll add a SECRET_KEY configuration to your application via the app.config object. Add it directly following the app definition before defining the index() view function:

flask_blog/app.py

. . .
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your secret key'


@app.route('/')
def index():
    conn = get_db_connection()
    posts = conn.execute('SELECT * FROM posts').fetchall()
    conn.close()
    return render_template('index.html', posts=posts)

. . .

Remember that the secret key should be a long random string.

After setting a secret key, you’ll create a view function that will render a template that displays a form you can fill in to create a new blog post. Add this new function at the bottom of the file:

flask_blog/app.py

. . .

@app.route('/create', methods=('GET', 'POST'))
def create():
    return render_template('create.html')

This creates a /create route that accepts both GET and POST requests. GET requests are accepted by default. To also accept POST requests, which are sent by the browser when submitting forms, you’ll pass a tuple with the accepted types of requests to the methods argument of the @app.route() decorator.

Save and close the file.

To create the template, open a file called create.html inside your templates folder:

  1. nano templates/create.html

Add the following code inside this new file:

flask_blog/templates/create.html

{% extends 'base.html' %}

{% block content %}
<h2>{% block title %} Create a New Post {% endblock %}h2>

<form method="post">
    <div class="form-group">
        <label for="title">Titlelabel>
        <input type="text" name="title"
               placeholder="Post title" class="form-control"
               value="{{ request.form['title'] }}">input>
    div>

    <div class="form-group">
        <label for="content">Contentlabel>
        <textarea name="content" placeholder="Post content"
                  class="form-control">{{ request.form['content'] }}textarea>
    div>
    <div class="form-group">
        <button type="submit" class="btn btn-primary">Submitbutton>
    div>
form>
{% endblock %}

Most of this code is standard HTML. It will display an input box for the post title, a text area for the post content, and a button to submit the form.

The value of the post title input is {{ request.form['title'] }} and the text area has the value {{ request.form['content'] }}, this is done so that the data you enter does not get lost if something goes wrong. For example, if you write a long post and you forget to give it a title, a message will be displayed informing you that the title is required. This will happen without losing the post you wrote since it will be stored in the request global object that you have access to in your templates.

Now, with the development server running, use your browser to navigate to the /create route:

http://127.0.0.1:5000/create

You will see a Create a New Post page with a box for a title and content.

How to create a website using python flask

This form submits a POST request to your create() view function. However, there is no code to handle a POST request in the function yet, so nothing happens after filling in the form and submitting it.

You’ll handle the incoming POST request when a form is submitted. You’ll do this inside the create() view function. You can separately handle the POST request by checking the value of request.method. When its value is set to 'POST' it means the request is a POST request, you’ll then proceed to extract submitted data, validate it, and insert it into your database.

Open the app.py file for editing:

  1. nano app.py

Modify the create() view function to look exactly as follows:

flask_blog/app.py

. . .

@app.route('/create', methods=('GET', 'POST'))
def create():
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']

        if not title:
            flash('Title is required!')
        else:
            conn = get_db_connection()
            conn.execute('INSERT INTO posts (title, content) VALUES (?, ?)',
                         (title, content))
            conn.commit()
            conn.close()
            return redirect(url_for('index'))

    return render_template('create.html')

In the if statement you ensure that the code following it is only executed when the request is a POST request via the comparison request.method == 'POST'.

You then extract the submitted title and content from the request.form object that gives you access to the form data in the request. If the title is not provided, the condition if not title would be fulfilled, displaying a message to the user informing them that the title is required. If, on the other hand, the title is provided, you open a connection with the get_db_connection() function and insert the title and the content you received into the posts table.

You then commit the changes to the database and close the connection. After adding the blog post to the database, you redirect the client to the index page using the redirect() function passing it the URL generated by the url_for() function with the value 'index' as an argument.

Save and close the file.

Now, navigate to the /create route using your web browser:

http://127.0.0.1:5000/create

Fill in the form with a title of your choice and some content. Once you submit the form, you will see the new post listed on the index page.

Lastly, you’ll display flashed messages and add a link to the navigation bar in the base.html template to have easy access to this new page. Open the template file:

  1. nano templates/base.html

Edit the file by adding a new

  • tag following the About link inside the