loader

Building Web Applications with Flask in Python 3: A Comprehensive Guide

Building Web Applications with Flask in Python 3: A Comprehensive Guide

Introduction:

Flask is like a toolbox for making websites with Python. It’s lightweight and easy to use, perfect for beginners. With Flask, you can build a website using just one Python file, which is great for getting started quickly. You don’t need to worry about complicated setup or following strict rules.

In this tutorial, we’ll also use something called Bootstrap to make our website look nice. Bootstrap helps us make our website work well on both computers and phones without having to write lots of extra code. This way, we can focus on learning Flask without getting bogged down in other stuff.

Flask uses something called Jinja to help make our web pages. It’s like a tool that lets us mix Python with HTML, making it easier to create dynamic web pages. We can use Python stuff like variables and loops right in our HTML!

In this tutorial, we’ll make a simple blog using Flask and SQLite in Python. Our blog will let people see all the posts we’ve written, and they can click on a post to read it. They’ll also be able to add new posts, and edit or delete ones that are already there.

What You Need Before You Start

Before you dive into this guide, make sure you have:

  1. A setup on your computer for running Python 3. You can set this up by following our guide for your specific computer type. We’ll name the folder where we’ll work ‘flask_blog’.

  2. Some basic knowledge of Python 3. This includes understanding things like different kinds of data, how to make choices in your code, looping through data, creating functions, and other important concepts. If you’re not familiar with Python yet, you can learn from our beginner’s guide on how to code in Python 3.

Step - 1 : Getting Flask Ready

In this step, we’ll get Flask installed and ready to go on your computer.

If you haven’t already, you need to turn on your Python environment. Just make sure you’re in the folder where you want to work on your project (called ‘flask_blog’). Then, use this command to turn on the environment:

source env/bin/activate
Copy

Once you’ve turned on your programming environment, you’ll notice a change in your command prompt. It might now have a little something extra at the start, like this:

(env)sammy@localhost:$
Copy

This extra bit at the start of your command prompt, like “env”, just tells you that your special programming environment is now active. The name might be different if you called it something else when you set it up.

(Note: If you’re using Git, a tool for keeping track of changes in your project, you can manage your project’s development more effectively. You can learn how to use Git by checking out our guide called “Introduction to Git: Installation, Usage, and Branches”.

When you’re using Git, it’s smart to tell it to ignore the “env” folder that was just created. This way, Git won’t keep track of files that aren’t really part of your project.)

Now, let’s get some Python tools installed and keep your project separate from the rest of your Python setup. We’ll use two tools called pip and python.

To get Flask installed, just type this command:

pip install flask
Copy

After everything’s installed, just type this command to make sure Flask is ready to go:

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

You’ll use a tool called the Python command line interface along with a special option (-c) to run some Python code. First, you’ll bring in the Flask package by typing import flask;. Then, you’ll print out the version of Flask by typing print(flask.version).

When you hit enter, you should see a version number show up, something like this:

Output
1.1.2
Copy

Step 2 — Building Your First Flask App

Now that everything’s set up, it’s time to start using Flask. In this step, you’re going to create a simple web application inside a Python file. When you run this file, it’ll start a server that shows some information in your web browser.

To get started, go to the folder named flask_blog on your computer. Inside that folder, find a file called hello.py and open it up for editing. You can use a program like nano or any text editor you like.

nano hello.py
Copy

In this hello.py file, we’re going to create a simple example of how to deal with requests on the web. Inside this file, we’ll bring in something called the Flask object, and then we’ll make a function that sends back a response when someone visits our website. Let’s write down the following code inside hello.py:

from flask import Flask

app = Flask(__name__)


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

In the code block above, we start by bringing in something called the Flask object from a package called flask. Then, we create our Flask application and give it a name, ‘app’. We use a special variable called name to help Flask know where our application is located.

Once we’ve created our app, we use it to handle requests from the web and send back responses. The @app.route decorator is like a label that tells Flask which function should respond to requests for a specific URL. In this case, we’re saying that the hello() function should respond when someone visits the main URL ‘/’.

Inside the hello() function, we simply return the phrase ‘Hello, World!’ as the response.

Now, save and close the file.

To run your web application, you need to tell Flask where to find it. You do this by setting an environment variable called FLASK_APP to the name of your file (hello.py in this case):.

export FLASK_APP=hello
Copy

In the code block above, we start by bringing in something called the Flask object from a package called flask. Then, we create our Flask application and give it a name, ‘app’. We use a special variable called __name__ to help Flask know where our application is located.

Once we’ve created our app, we use it to handle requests from the web and send back responses. The @app.route decorator is like a label that tells Flask which function should respond to requests for a specific URL. In this case, we’re saying that the hello() function should respond when someone visits the main URL ‘/’. 

Inside the hello() function, we simply return the phrase ‘Hello, World!’ as the response.

Now, save and close the file.

To run your web application, you need to tell Flask where to find it. You do this by setting an environment variable called FLASK_APP to the name of your file (hello.py in this case):

export FLASK_APP=hello
Copy

Then, set the FLASK_ENV environment variable to development mode to run your app:

export FLASK_ENV=development
Copy

Finally, start your application by using the flask run command:

flask run
Copy

When the application is running, you’ll see an output that looks 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
Copy

The output you see has important information like:

– The name of the app you’re running.

– The environment it’s running in.

– “Debug mode: on” means the Flask debugger is active. This helps you see detailed error messages, making it easier to fix problems.

– The app is running locally at the URL http://127.0.0.1:5000/. Here, 127.0.0.1 is the address for your computer’s local server, and 5000 is the port number.

To see your app in action, open a web browser and go to http://127.0.0.1:5000/. You should see the message “Hello, World!” which means your app is working correctly.

(Warning: When you’re working on your Flask app, it uses a basic web server to show your application in a development setting. This setup also includes the Flask debugger to help you spot and fix errors easily. But remember, this development server isn’t meant for putting your app live for lots of people to see. If you want to learn more about how to put your Flask app into action for a bigger audience, check out the Flask documentation’s Deployment Options page. You can also explore a Flask deployment tutorial for step-by-step guidance.)

Now, if you want to keep your development server running, you can leave it in the terminal window and open another terminal window. Go to the folder where your hello.py file is located. Activate the virtual environment if you haven’t already, and set the environment variables FLASK_ENV and FLASK_APP, just like we did before. Once you’ve done that, you can move on to the next steps of your project.

(Note: If you’re opening a new terminal, make sure to activate your virtual environment and set the environment variables FLASK_ENV and FLASK_APP again.

Keep in mind, if you already have a Flask app running in the development server, you can’t start another Flask app using the same command (flask run). This is because Flask uses port number 5000 by default, and once it’s being used, you can’t use it again for another app. If you try, you’ll get an error message.

Output
OSError: [Errno 98] Address already in use
Copy

To fix this issue, you have a couple of options. First, you can stop the server that’s already running by pressing CTRL+C, then start your new Flask app with flask run again.

Alternatively, if you want to run both apps at the same time, you can use a different port number for the second app. You do this by adding the -p argument followed by the port number you want. For example, to run another app on port 5001, you’d use this command:

flask run -p 5001
Copy

Now you’ve got a simple Flask web app up and running. You’ve seen it in action by displaying information in your web browser. Next up, you’ll learn how to use HTML files in your app.

Step 3 — Adding HTML to Your Flask App

Right now, your application just shows a basic message without any fancy formatting. But most web apps use HTML to make things look nice for visitors. So, in this step, you’ll start adding HTML files to your app to make it look more like a real website.

Flask makes it easy to work with HTML using something called the Jinja template engine. This lets you write your HTML code in separate .html files and even add some logic to your HTML. These HTML files, also called templates, will be used to create all the different pages of your app, like the main page where you show your blog posts, the page for each blog post, and the page where users can add new posts.

To get started, let’s create a new file for your Flask app. Go to your flask_blog folder and open up a new file called app.py using nano or any text editor you like. This file will hold all the code for your blogging application.

nano app.py
Copy

In this new file, you’ll first import the Flask object to create your Flask app, just like you did before. You’ll also import the render_template() function, which helps you show HTML files on your website. These HTML files will be stored in a folder called templates that you’ll create soon.

This file will have one function that handles requests to the main URL (“/”). Add the following code to the file:

from flask import Flask, render_template

app = Flask(__name__)

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

In the index() function, we use render_template() to show a file called index.html, but we haven’t made that file or the templates folder yet. If you try to run the app now, you’ll get an error. We’ll run it anyway to see what the error looks like, then we’ll fix it by creating the folder and file we need.

Save and close the file.

Now, stop the development server in the other terminal where you were running the hello app by pressing CTRL+C.

Before you start the new app, make sure you set the FLASK_APP environment variable correctly, since we’re not using the hello app anymore:

export FLASK_APP=app
flask run
Copy

When you open http://127.0.0.1:5000/ in your browser, you’ll see a page from the debugger telling you that it couldn’t find the index.html template. It’ll even highlight the line of code where this error happened, which is the one that says return render_template(‘index.html’).

If you click on that line, the debugger will show you more of the code around it. This can help you figure out what’s causing the problem and how to fix it.

To solve this problem, first, make a new folder called “templates” inside your flask_blog directory. Then, inside the “templates” folder, create a new file called “index.html” and open it for editing.

mkdir templates
nano templates/index.html
Copy

Next, you’ll need to put some HTML code inside the “index.html” file. Here’s what you should add:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>FlaskBlog</title>
</head>
<body>
   <h1>Welcome to FlaskBlog</h1>
</body>
</html>

Copy

After saving the file, go back to your browser and visit http://127.0.0.1:5000/ again, or simply refresh the page. This time, you should see the text “Welcome to FlaskBlog” displayed in a big header (marked by <h1> tags).

Besides the “templates” folder, Flask apps usually have a “static” folder for holding files like CSS, JavaScript, and images used by the app.

To add some style to your app, let’s create a CSS file named “style.css”. First, make a new folder called “static” inside your main “flask_blog” directory:

mkdir static
Copy

Next, you’ll make another directory called “css” inside the “static” directory to store your CSS files. This helps keep things organized. Similarly, you might have a “js” directory for JavaScript files and an “images” or “img” directory for images.

Here’s the command to create the “css” directory inside the “static” directory:

mkdir static/css
Copy

Now, open a file named “style.css” inside the “css” directory. You’ll use this file to write your CSS code:

nano static/css/style.css
Copy

Now, add the following CSS rule to your “style.css” file:

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

Next, let’s open the “index.html” template file again for editing:

nano templates/index.html
Copy

Now, you’ll include a link to the “style.css” file in the <head> section of the “index.html” template file:

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

Copy

In this step, you used the url_for() function to generate the correct location for the file. The first argument tells Flask that you’re linking to a static file, and the second argument is the path of the file inside the static directory.

Now, save and close the file.

When you refresh the index page of your app, you’ll see that the text “Welcome to FlaskBlog” is now brown, centered, and has a border around it.

If you’re not familiar with CSS or web design, you can use the Bootstrap toolkit to style your app. Bootstrap provides ready-to-use components for styling. In this project, we’ll use Bootstrap.

When you create another HTML template, you might find yourself repeating a lot of the HTML code you already wrote. To avoid this, you can create a base template that all your HTML files will inherit from. This saves you from repeating code unnecessarily.

To make a base template, start by creating a file called “base.html” inside your “templates” directory:

nano templates/base.html
Copy

Now, let’s add the following code to your “base.html” template:

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

    <!-- Bootstrap CSS -->
    <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')}}">FlaskBlog</a>
        <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="#">About</a>
            </li>
            </ul>
        </div>
    </nav>
    <div class="container">
        {% block content %} {% endblock %}
    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <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>

Copy

Once you’re done editing, remember to save and close the file.

Most of the code you see is standard HTML and code needed for Bootstrap. The <meta> tags give information to web browsers, the <link> tag links to Bootstrap CSS files, and the <script> tags link to JavaScript code for additional Bootstrap features. You can learn more about these in the Bootstrap documentation.

However, there are some parts highlighted below that are specific to the Jinja template engine:

  • {% block title %} {% endblock %}: This creates a placeholder for a title. You’ll use this in other templates to give each page a custom title without having to rewrite the entire <head> section each time.
  • {{ url_for(‘index’)}}: This function returns the URL for the index() view function. It’s different from the previous url_for() call we used to link a static CSS file because it only takes one argument, which is the name of the view function. It links to the route associated with that function, not a static file.
  • {% block content %} {% endblock %}: This creates another placeholder that will be replaced by content depending on the child template (templates that inherit from base.html). Each child template can override this block with its own content.

Now, let’s open the “index.html” file to take advantage of the base template and inheritance.

nano templates/index.html
Copy

Next, replace the contents of “index.html” with the following code:

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
{% endblock %}
Copy

In this updated version of the index.html template, you’re using the {% extends %} tag to inherit from the base.html template. This means you’re bringing in all the code from the base template.

Inside the {% block content %} block, you’re replacing the content that was originally in the base template’s content block. Here, you have an <h1> tag with the text “Welcome to FlaskBlog” inside a title block. This replaces the original title block in the base.html template with your custom text. This way, you avoid repeating the same text twice—it serves as both the title for the page and a heading below the navigation bar inherited from the base template.

Template inheritance lets you reuse HTML code from other templates (like base.html) without having to rewrite it each time.

After saving and closing the file, refresh the index page in your browser. You’ll see your page with a navigation bar and a styled title.

You’ve learned how to use HTML templates and static files in Flask, along with Bootstrap to improve the appearance of your page. By implementing a base template, you’ve minimized code repetition. 

Next up, you’ll configure a database to store your application data.

Step 4 — Database Setup

In this step, you’ll establish a database to manage your application’s data, specifically the blog posts. Additionally, you’ll populate the database with some sample entries.

To handle the database, you’ll use SQLite. It’s a convenient choice because the sqlite3 module, which we’ll utilize to interact with the database, is included in Python’s standard library. If you want to learn more about SQLite, you can refer to this tutorial.

Firstly, since SQLite organizes data into tables and columns, and our data primarily consists of blog posts, we’ll start by creating a table named ‘posts’ with the necessary columns. We’ll create a .sql file containing SQL commands to define the structure of the ‘posts’ table. Later, we’ll use this file to create the database.

Begin by creating a file named ‘schema.sql’ in your ‘flask_blog’ directory:

nano schema.sql
Copy

Write these commands in a file using SQL:

DROP TABLE IF EXISTS posts;

CREATE TABLE posts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    title TEXT NOT NULL,
    content TEXT NOT NULL
);
Copy

Save and close the document.

The first SQL directive ensures a fresh start by removing any existing ‘posts’ tables. This helps maintain clarity and organization.

Next, we establish a ‘posts’ table with specific components:

  • ‘id’: Every blog entry receives its unique identifier.
  • ‘created’: Indicates the time of post creation.
  • ‘title’: The heading of the post.
  • ‘content’: The primary information within the post.

Remember, implementing this directive will erase any existing data in the database. Therefore, refrain from adding essential content until you complete this tutorial.

Now, we’ll utilize this SQL arrangement to configure the database using a Python script named ‘init_db.py’ within the ‘flask_blog’ directory. Feel free to employ any text editor of your preference for this task.

nano init_db.py
Copy

Now, add the following code:

import sqlite3


connection = sqlite3.connect('database.db')




with open('schema.sql') as f:
    connection.executescript(f.read())


cur = connection.cursor()


cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
            ('First Post', 'Content for the first post')
            )


cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
            ('Second Post', 'Content for the second post')
            )


connection.commit()
connection.close()

Copy

First, you import a tool called ‘sqlite3’ that helps work with databases. Then, you connect to a new database file called ‘database.db’.

After that, you open another file called ‘schema.sql’, which contains instructions to set up the structure of our database. You run these instructions all at once using a special function called ‘executescript()’. This creates a table named ‘posts’ in the database.

Next, you make a ‘Cursor’ object, which helps you execute specific commands in the database. You use this to add two new blog posts into the ‘posts’ table.

Lastly, you save your changes and close the connection to the database.

Save and close your file. Then, in your terminal, type ‘python’ followed by the name of your file to run it.

python init_db.py
Copy

When the script finishes running, a new file called ‘database.db’ will appear in your ‘flask_blog’ folder. This shows that your database is ready.

Next, you will learn how to fetch the posts you added to the database and display them on the homepage of your app.

Step 5 — Showing All Blog Posts

With your database ready, you can now update the index() view function to show all the posts in your database.

Open the app.py file and make these changes:

nano app.py
Copy

First, add the sqlite3 module at the top of your file:

import sqlite3
from flask import Flask, render_template

. . .
Copy

After that, make a function that sets up a connection to the database and gives it back to you. Put this function right after the imports:

. . .
from flask import Flask, render_template

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

. . .
Copy

After making the get_db_connection() function, it opens a connection to the ‘database.db’ file and sets the row_factory to ‘sqlite3.Row’. This lets you access columns by name, like regular Python dictionaries. Then, the function gives back the ‘conn’ connection object, which you’ll use to work with the database.

Once you’ve done that, update the index() function to look like this:

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

In this new version of the index() function, you first connect to the database using the get_db_connection() function you made earlier. Then, you run a SQL query to get all the entries from the ‘posts’ table. You use the fetchall() method to get a list of all the posts you added to the database earlier.

After that, you close the database connection. Then, you send the index.html template as the result. You also send the ‘posts’ object, which has all the posts you got from the database. This lets you show the blog posts on the index.html template.

Once you’ve done these changes, save and close the app.py file.

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

Open the index.html file:

nano templates/index.html
Copy

After that, change it to look like this:

{% extends 'base.html' %}

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

Copy

Here, the code {% for post in posts %} is a Jinja loop, similar to a Python loop but it needs to be closed with {% endfor %}. This loop goes through each item in the ‘posts’ list passed by the index() function with return render_template(‘index.html’, posts=posts). Inside this loop, each post title is shown in an <h2> heading within an <a> tag (which will later link to each post).

The title is displayed using {{ … }}, which is a way to insert variables. Since ‘post’ is like a dictionary, you access the post title with post[‘title’]. The creation date of the post is also displayed using the same method.

After editing the file, save and close it. Then go to your browser and open the index page. You’ll see the two posts you added to the database on the page.

Since you’ve updated the index() function to show all your posts on the homepage, the next thing is to display each post separately on its own page. This allows users to click on a link to see each post individually.

Step 6 — Showing One Post at a Time

In this step, you will create a new route in Flask with a view function and a new HTML template to show a single blog post by its ID.

By the end of this step, the URL http://127.0.0.1:5000/1 will show the first post (because it has the ID 1). The URL http://127.0.0.1:5000/ID will show the post with that ID if it exists.

Open app.py to edit it:

nano app.py
Copy

Since you’ll want to locate a blog post by its ID from the database at various points in the project, you’ll craft a distinct function named get_post(). This function will accept an ID and provide the corresponding blog post, or present a 404 Not Found message if the post isn’t found.

To display a 404 error, import the abort() function from the Werkzeug library, which is included with Flask. Include this import statement at the beginning of the file:

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

. . .
Copy

Next, place the get_post() function immediately after the get_db_connection() function you made earlier:

. . .

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

. . .
Copy

This fresh function contains a post_id parameter that determines which blog post to retrieve.

Within this function, you utilize the get_db_connection() function to establish a connection to the database and execute an SQL query to fetch the blog post linked with the provided post_id. You utilize the fetchone() method to obtain the result and assign it to the post variable before closing the connection. If the post variable equals None, indicating no result was discovered in the database, you apply the abort() function you previously imported to return a 404 error code, and the function halts. However, if a post is discovered, you return the value stored in the post variable.

Following this, append the subsequent view function to the conclusion of the app.py file:

. . .

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

In this fresh perspective function, you introduce a variable guideline <int:post_id> to indicate that the segment after the slash (/) is a positive integer (denoted with the int converter), which you’ll require to access in your viewpoint function. Flask acknowledges this and sends its value to the post_id keyword argument of your post() perspective function. Then, you employ the fetch_blog() function to retrieve the weblog post associated with the specified ID and stash the consequence in the post variable. Subsequently, you transfer this variable to a post.html template, which you’ll promptly fabricate.

Preserve the app.py document and initiate a novel post.html template for modification:

nano templates/post.html
Copy

Write the following code in the new post.html file. It will resemble the index.html file, but instead of displaying multiple posts, it will only show a single post along with its content:

{% 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 %}

Copy

Add the title block from the base.html template to ensure that the page’s title matches the post title displayed in an <h2> heading.

After making these changes, save and close the file.

You can now visit the following URLs to view the two posts stored in your database, as well as a page informing the user that the requested blog post could not be found (as there is currently no post with an ID number of 3):

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

Returning to the index page, you’ll link each post title to its own page. To accomplish this, you’ll utilize the url_for() function. Begin by opening the index.html template for editing:

nano templates/index.html
Copy

Next, update the value of the href attribute from # to {{ url_for(‘post’, post_id=post[‘id’]) }} within the for loop. Ensure that the for loop appears exactly as follows:

{% 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 %}
Copy

Here, you hand over ‘post’ to the url_for() function as the initial argument. This denotes the post() view function, and since it requires a post_id argument, you furnish it with the value post[‘id’]. The url_for() function will generate the correct URL for each post based on its ID.

Upon saving and closing the file, the links on the index page will now guide users to their intended destinations. With this, you’ve completed constructing the section of the application responsible for exhibiting the blog posts from your database. Following this, you’ll introduce functionalities to create, modify, and erase blog posts in your application.

Step 7 — Editing Blog Posts

Since you’ve wrapped up displaying the blog posts stored in the database on your web app, it’s time to empower users to write fresh blog entries and include them in the database. Furthermore, users should have the capability to modify existing posts and remove those that are no longer necessary.

Adding a New Blog Post

Until now, your application shows the posts stored in your database but lacks a method to add a new post unless you manually access the SQLite database and add one. In this section, you’ll design a page where you can easily create a post by entering its title and content.

Open the app.py file to make changes:

nano app.py
Copy

Initially, you’ll include the following imports from the Flask framework:

 

– The global request object: It’s utilized to access incoming request data submitted through an HTML form.

– The url_for() function: It’s employed to generate URLs.

– The flash() function: This is for displaying a message when a request is handled.

– The redirect() function: It redirects the client to another location.

 

Insert these imports into your file as shown below:

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

. . .
Copy

The flash() method stores flashed messages within the user’s browser session, necessitating the creation of a confidential key. This key is crucial for securing sessions, enabling Flask to retain information across requests, such as transitioning from the new post page to the index page. Although users can access session data, they cannot alter it without the secret key. Therefore, it’s vital to safeguard your confidential key and refrain from sharing it with anyone. For additional insights on sessions, refer to the Flask documentation.

To establish a confidential key, introduce a SECRET_KEY configuration into your application via the app.config object. Add it immediately after defining the app, preceding the definition of the index() view function:

. . .
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)

. . .
Copy

Keep in mind that the secret key should be a lengthy and random string.

Once you’ve set the secret key, you’ll develop a view function to present a template containing a form. This form allows you to input information to create a new blog post. Insert this new function at the end of the file:

. . .

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

This establishes a /create route that accommodates both GET and POST requests. By default, it accepts GET requests. To also allow POST requests, which are transmitted by the browser when forms are submitted, you’ll specify a tuple with the accepted types of requests in the methods parameter of the @app.route() decorator.

After making these changes, save and close the file.

To craft the template, open a file named create.html within your templates folder:

nano templates/create.html
Copy

Insert the following code into this fresh document:

{% extends 'base.html' %}

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

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

    <div class="form-group">
        <label for="content">Content</label>
        <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">Submi<
    </div>
</form>
{% endblock %}

Copy

The majority of this code consists of standard HTML. It constructs an input field for the post title, a text area for the post content, and a button to submit the form.

The post title input field is pre-filled with {{ request.form[‘title’] }}, while the text area contains {{ request.form[‘content’] }}. This ensures that any input data isn’t lost if an error occurs. For example, if you write a lengthy post but forget to add a title, a message will prompt you to enter one. However, your written post won’t vanish; it’s stored in the request global object accessible within your templates.

Now, while the development server is running, open your browser and navigate to the /create route:

http://127.0.0.1:5000/create
Copy

You’ll encounter a page titled “Create a New Post” featuring fields for entering a title and content.

This form triggers a POST request to your create() view function. However, currently, there’s no code to manage a POST request within the function. Thus, nothing occurs after completing the form and submitting it.

To handle the incoming POST request upon form submission, you’ll address it within the create() view function. You’ll manage the POST request distinctly by evaluating the value of request.method. If it reads ‘POST’, indicating a POST request, you’ll then proceed to extract the submitted data, validate it, and insert it into your database.

Open the app.py file for modifications:

nano app.py
Copy

Update the create() view function to match the following exactly:

. . .

@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')

Copy

Within the if statement, you confirm that the subsequent code executes solely when the request is a POST request, which is checked by comparing request.method to ‘POST’.

Afterward, you extract the submitted title and content from the request.form object, which provides access to the form data within the request. If no title is provided, the condition if not title is met, triggering a message to inform the user about the necessity of a title. Conversely, if a title is provided, you establish a connection using the get_db_connection() function and insert both the title and content into the posts table.

Subsequently, you save the changes to the database and close the connection. Upon adding the blog post to the database, you use the redirect() function to direct the client to the index page. This function takes the URL generated by the url_for() function, with ‘index’ passed as an argument.

Save and close the file.

Then, access the /create route in your web browser:

http://127.0.0.1:5000/create
Copy

Enter your desired title and content into the form. After submitting the form, you’ll notice the new post appearing on the index page.

Finally, you’ll showcase flashed messages and include a link in the navigation bar within the base.html template for convenient access to this new page. Open the template file:

nano templates/base.html
Copy

Revise the file by inserting a fresh <li> tag after the About link within the <nav> section. Subsequently, integrate a new loop immediately above the content section to exhibit the flashed messages underneath the navigation bar. These messages are accessible through Flask’s dedicated get_flashed_messages() function:

<nav class="navbar navbar-expand-md navbar-light bg-light">
    <a class="navbar-brand" href="{{ url_for('index')}}">FlaskBlog</a>
    <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">
            <a class="nav-link" href="#">About
        </li>
        <li class="nav-item">
            <a class="nav-link" href="{{url_for('create')}}">New Post</a>
        </li>
        </ul>
    </div>
</nav>
<div class="container">
    {% for message in get_flashed_messages() %}
        <div class="alert alert-danger">{{ message }}</div>
    {% endfor %}
    {% block content %} {% endblock %}
</div>

Copy

Save the changes and then close the file. Now, the navigation bar will feature a “New Post” option that directs to the /create route.

Updating a Post

To keep your blog current, it’s essential to have the capability to edit your existing posts. This section will walk you through creating a new page in your application to streamline the post editing process.

Initially, you’ll incorporate a fresh route into the app.py file. Its associated view function will receive the post ID requiring editing. The URL structure will be in the format /post_id/edit, where post_id represents the ID of the post. Open the app.py file to make these adjustments:

nano app.py
Copy

Then, append the following edit() view function at the bottom of the file. Modifying an existing post is akin to creating a new one, hence this view function will resemble the create() view function:

. . .

@app.route('//edit', methods=('GET', 'POST'))
def edit(id):
    post = get_post(id)

    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('UPDATE posts SET title = ?, content = ?'
                         ' WHERE id = ?',
                         (title, content, id))
            conn.commit()
            conn.close()
            return redirect(url_for('index'))

    return render_template('edit.html', post=post)
Copy

The post you wish to modify is determined by the URL, with Flask passing the ID number to the edit() function via the id parameter. Integrate this value into the get_post() function to retrieve the corresponding post from the database based on the provided ID. Incoming data arrives in a POST request, which is managed within the if request.method == ‘POST’ condition.

Similar to creating a new post, start by extracting data from the request.form object. If the title field is empty, display a message; otherwise, establish a connection with the database. Proceed to update the posts table by setting a new title and content where the post’s ID in the database matches the ID in the URL.

For a GET request, render an edit.html template, passing the post variable containing the result of the get_post() function. This data will be utilized to present the current title and content on the edit page.

Upon saving and closing the file, proceed to create a new edit.html template:

nano templates/edit.html
Copy

Enter the following code within this fresh file:

{% extends 'base.html' %}

{% block content %}
<h1>{% block title %} Edit "{{ post['title'] }}" {% endblock %}</h1>

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

    <div class="form-group">
        <label for="content">Content</label>
        <textarea name="content" placeholder="Post content"
                  class="form-control">{{ request.form['content'] or post['content'] }}</textarea>
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-primary">Submit</button>
    </div>
</form>
<hr>
{% endblock %}

Copy

Save the changes and then close the file.

This code operates similarly, except for the {{ request.form[‘title’] or post[‘title’] }} and {{ request.form[‘content’] or post[‘content’] }} sections. Here, if data exists in the request, it’s displayed; otherwise, the data from the post variable, containing current database information, is displayed.

Next, go to the following URL to edit the initial post:

http://127.0.0.1:5000/1/edit
Copy

Revise the post and submit the form. Afterward, confirm that the post has been successfully updated.

Now, you must incorporate a link directing to the edit page for each post on the index page. Open the index.html template file:

nano templates/index.html
Copy

Modify the file to precisely match the following content:

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
    {% 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>
        <a href="{{ url_for('edit', id=post['id']) }}">
            <span class="badge badge-warning">Edit</span>
        </a>
        <hr>
    {% endfor %}
{% endblock %}

Copy

Save your changes, then close the file.

In this adjustment, you incorporate an <a> tag to connect to the edit() view function, including the post[‘id’] value to link to the edit page of each post with the “Edit” link.

Removing a Post

At times, there arises a need to remove a post from public view. Hence, integrating the capability to delete a post is essential. This step focuses on adding the delete functionality to your application.

Initially, you’ll incorporate a new route at /ID/delete, designed to handle POST requests akin to the edit() view function. The newly created delete() view function will obtain the post ID to be deleted from the URL. Open the app.py file:

nano app.py
Copy

Append the subsequent view function at the end of the file:

# ....

@app.route('//delete', methods=('POST',))
def delete(id):
    post = get_post(id)
    conn = get_db_connection()
    conn.execute('DELETE FROM posts WHERE id = ?', (id,))
    conn.commit()
    conn.close()
    flash('"{}" was successfully deleted!'.format(post['title']))
    return redirect(url_for('index'))
Copy

This function exclusively accepts POST requests. Trying to access the /ID/delete path directly in your browser will lead to an error because browsers usually default to GET requests.

However, you can reach this path through a form that sends a POST request, including the post’s ID you wish to delete. Once the function receives the ID value, it retrieves the corresponding post from the database using the get_post() function.

Then, it initiates a connection to the database and executes a DELETE FROM SQL command to eliminate the post. The function then confirms the database change, closes the connection, flashes a message indicating successful post deletion, and redirects the user to the index page.

It’s important to note that no template file is rendered during this process. Instead, you’ll simply add a Delete button to the edit page.

Open the edit.html template file:

nano templates/edit.html
Copy

After the <hr> line and just before the {% endblock %} line, insert the following <form> code:

<hr>

<form action="{{ url_for('delete', id=post['id']) }}" method="POST">
    <input type="submit" value="Delete Post"
            class="btn btn-danger btn-sm"
            onclick="return confirm('Are you sure you want to delete this post?')">
</form>

{% endblock %}

Copy

When you want to make sure you’re really going to submit something, you can use the confirm() method to show a message asking if you’re sure.

Now, go back to the page where you can change a blog post and try to remove it.

http://127.0.0.1:5000/1/edit
Copy

Once you finish this step, your project’s code will resemble the code shown on this page.

Now, people using your app can do more things like writing new blog posts and saving them in the database. They can also edit and remove posts that are already there.

Conclusion:

This guide taught you some important ideas about the Flask Python framework. You discovered how to create a simple web app, run it using a development server, and let users input their own data through URLs and web forms. You also used Jinja to reuse HTML files and add logic to them.

Now, thanks to this guide, you’ve got a fully working web blog! It can create, show, edit, and delete blog posts using Python and SQL commands with an SQLite database.

If you want to dive deeper into Flask and SQLite, check out this tutorial on How To Use One-to-Many Database Relationships with Flask and SQLite.

But wait, there’s more you can do with your blog app! You can make it even better by adding user authentication. This means only registered users can make or change blog posts. You could also let users comment on posts and add tags. And why not let them upload images too?

To learn more, check out the Flask documentation. Plus, there are lots of helpful Flask extensions made by the community:

– Flask-Login: It handles user sessions, login, logout, and remembering users.

– Flask-SQLAlchemy: Makes using Flask with SQLAlchemy (a Python SQL toolkit) easier for working with databases.

– Flask-Mail: This one helps you send emails from your Flask app.

Why not give some of these a try and make your blog app even cooler?

image

A cloud for entire journey

Bring your team together. No contracts, no commitments.

image

Copyright ©2023 Design & Developed by Cloudtopiaa