How To Build A Blog Website Using Flask In Python

How To Build A Blog Website Using Flask In Python

·

28 min read

Introduction

Flask is a lightweight micro web development framework developed and written in Python. It is easy to learn and use. In this tutorial, we will be building a simple blog website using the Flask web framework, Flask_SQLAlchemy for database management, Flask-Login for user management and Html and CSS for the frontend aspect of the development.

We will cover the following topics as we proceed:

  • Setting up a project folder and virtual environment Installing

  • Installing Flask and its dependencies

  • Creating a Flask app and preparing a database

  • Creating HTML templates and styling with CSS

  • Registering, logging in, and logging out a user

  • Displaying, posting, editing, and deleting articles

  • User authentication and authorization: making sure only the author of an article can edit or delete it. Let's get started!

Prerequisites

It is important to have the following set up on your device:

You should also be familiar with the concepts of HTML and CSS else u can take a free tutorial on W3Schools

Initial Preparation

First, create a project folder and navigate to it in your terminal.

mkdir my_first_blog.db
cd my_first_blog.db

Next, set up a virtual environment using the following command:

python -m venv venv

This will create a venv folder in your project directory, which will contain the virtual environment.

To activate the virtual environment, use the following command:

# Windows
source venv\Scripts\activate
# Unix or Linux
source venv/bin/activate

Install Flask

To install Flask, Flask-sqlAlchemy and Flask-Login, we use a package installer for python simply known as "pip".

pip install flask flask-sqlalchemy flask-login

This will install Flask, Flask-SQLAlchemy, and Flask-Login respectively.

Create A Flask App

  • In the root of your project folder, create a file called app.py and add the following code:
from flask import Flask

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

if __name__ == '__main__':
    app.run()

This creates a basic Flask app that displays "Hello, World!" at the root URL ('/').

To run the app, use the following command:

python app.py

This will start the development server at http://127.0.0.1:5000. Visit this URL in your web browser to see the "Hello, World!" message.

Prepare a Database

Next, let's set up a database using Flask-SQLAlchemy.

Add the following imports to the top of app.py:

import os
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

app.config['SQLALCHEMY_DATABASE_URI'] ='sqlite:///' + os.path.join(basedir, 'my_first_blog.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config["SECRET_KEY"] = "my_secret_key"

db = SQLAlchemy(app)
login_manager = LoginManager(app)

This sets up an SQLite database at `my_first_blog.db

Creating Basic Database Models

Now, we'll create some basic database models using Flask-SQLAlchemy. A model is a class that represents a table in the database.

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password = db.Column(db.String(120), nullable=False)

class Article(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(80), nullable=False)
    content = db.Column(db.Text, nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    user = db.relationship('User', backref=db.backref('articles', lazy=True))

class Comment(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.Text, nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    article_id = db.Column(db.Integer, db.ForeignKey('article.id'), nullable=False)
    user = db.relationship('User', backref=db.backref('comments', lazy=True))
    article = db.relationship('Article', backref=db.backref('comments', lazy=True))

class Like(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    article_id = db.Column(db.Integer, db.ForeignKey('article.id'), nullable=False)
    user = db.relationship('User', backref=db.backref('likes', lazy=True))
    article = db.relationship('Article', backref=db.backref('likes', lazy=True))

These models will allow you to store and manage users, articles, comments, and likes in your database. You can then use them to create routes and functions in your Flask app to perform various actions, such as creating a new user, posting an article, and leaving a comment.

Design the Basic Routes for the App

These are just a few examples of the basic routes that you might want to include in your Flask app. You can add more routes as needed to handle additional actions, such as posting new articles or leaving comments.

Here's an example of how you might define these routes in your Flask app:

@app.route('/')
def home():
    articles = Article.query.all()
    return render_template('home.html', articles=articles)

@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegistrationForm()
    if form.validate_on_submit():
        # create a new user
        return redirect(url_for('home'))
    return render_template('register.html', form=form)

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        # log in the user
        return redirect(url_for('home'))
    return render_template('login.html', form=form)

@app.route('/logout')
def logout():
    # log out the user
    return redirect(url_for('home'))

@app.route('/articles/<article_id>')
def article(article_id):
    article = Article.query.get(article_id)
    comments = Comment.query.filter_by(article_id=article_id).all()
    return render_template('article.html', article=article, comments=comments)

Creating HTML Templates

HTML templates allow us to create a consistent layout and design for our web application. We can use a templating engine like Jinja to insert dynamic content into our templates and create a more interactive and user-friendly website.

In this section, we'll go over how to create HTML templates for our blog website using Jinja. We'll create a base template, an index template, and a template for each of the routes that we defined earlier (login, signup, and protected).

Base.html

The base template will define the basic structure of our website, including the header, footer, and navigation menu. We can use Jinja template inheritance to extend this base template and create more specific templates for different pages of our website.

To create a base template, create a new file called "base.html" in a "templates" folder in your project directory. Then, add the following code:

<!doctype html>
<html>
  <head>
    <title>{% block title %} {% endblock %}</title>
  </head>
  <body>
    <header>
      <nav>
        <ul>
          <li><a href="/home">Home</a></li>
          <li><a href="/login">Login</a></li>
          <li><a href="/signup">Signup</a></li>
          {% if current_user.is_authenticated %}
            <li><a href="/protected">Protected</a></li>
            <li><a href="/logout">Logout</a></li>
          {% endif %}
        </ul>
      </nav>
    </header>
    <main>
      {% for message in get_flashed_messages() %}
        <div class="flash">{{ message }}</div>
      {% endfor %}
      {% block content %} {% endblock %}
    </main>
    <footer>
      Copyright {{ current_year }}
    </footer>
  </body>
</html>

In this template, we've defined a block called "title" which will be used to specify the title of each page, a block called "content" which will be used to insert the content of each page, and a navigation menu that includes links to the home, login, and signup pages. We've also included a block of code that checks if the user is authenticated and, if they are, displays a link to the protected page and a logout button.

Index.html

The index template will be used to display the home page of our website. To create an index template, create a new file called "index.html" in the "templates" folder. Then, add the following code:

{% extends "base.html" %}

{% block title %} Home {% endblock %}

{% block content %}
  <h1>Welcome to our blog website!</h1>
  {% for article in articles %}
    <h2>{{ article.title }}</h2>
    <p>{{ article.content }}</p>
  {% endfor %}
{% endblock %}

In this template, we've extended the base template and defined a block called "content" which includes a loop that iterates over a list.

Login.html

The login template will be used to display the login form for our website. To create a login template, create a new file called "login.html" in the "templates" folder. Then, add the following code:

{% extends "base.html" %}

{% block title %} Login {% endblock %}

{% block content %}
  <h1>Login</h1>
  <form method="post" action="/login">
    <label for="email">Email:</label><br>
    <input type="email" id="email" name="email"><br>
    <label for="password">Password:</label><br>
    <input type="password" id="password" name="password"><br><br>
    <input type="submit" value="Submit">
  </form> 
{% endblock %}

In this template, we've extended the base template and defined a block called "content" which includes a login form with fields for the user's email and password.

Signup.html

The signup template will be used to display the signup form for our website. To create a signup template, create a new file called "signup.html" in the "templates" folder. Then, add the following code:

{% extends "base.html" %}

{% block title %} Signup {% endblock %}

{% block content %}
  <h1>Signup</h1>
  <form method="post" action="/signup">
    <label for="username">Username:</label><br>
    <input type="text" id="username" name="username"><br>
    <label for="email">Email:</label><br>
    <input type="email" id="email" name="email"><br>
    <label for="password">Password:</label><br>
    <input type="password" id="password" name="password"><br><br>
    <input type="submit" value="Submit">
  </form> 
{% endblock %}

In this template, we've extended the base template and defined a block called "content" which includes a signup form with fields for the user's username, email, and password.

Protected.html

The protected template will be used to display a page that is only accessible to authenticated users. To create a protected template, create a new file called "protected.html" in the "templates" folder. Then, add the following code:

{% extends "base.html" %}

{% block title %} Protected {% endblock %}

{% block content %}
  <h1>Protected Page</h1>
  <p>This page is only accessible to authenticated users.</p>
{% endblock %}

In this template, we've extended the base template and defined a block called "content" which includes a message indicating that the page is only accessible to authenticated users.

Styling with CSS using Bootstrap

CSS (Cascading Style Sheets) is a stylesheet language used for describing the look and formatting of a document written in HTML. It is a powerful tool for adding style and layout to web applications.

Bootstrap is a popular front-end framework that provides a collection of CSS styles and layout classes for building responsive, mobile-first websites. It includes a set of predefined CSS styles that can be easily applied to your HTML elements to create a consistent, professional-looking design.

To use Bootstrap in your Flask application, you can include the Bootstrap CSS and JavaScript files in your HTML templates. You can either link to the Bootstrap CDN (Content Delivery Network) or download the files and include them locally.

To link to the Bootstrap CDN, add the following lines to the head of your HTML templates:

<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">

<!-- Latest compiled JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>

To use the Bootstrap styles in your HTML elements, you can apply the appropriate Bootstrap classes to your elements. For example, to create a container element that centers its contents and applies to padding, you can use the container and p-5 classes like this:

<div class="container p-5">
  <!-- content goes here -->
</div>

To create a button with the Bootstrap style, you can use the btn and btn-primary classes like this:

<button class="btn btn-primary">Click me!</button>

There are many more Bootstrap styles and layout classes that you can use to style your HTML elements. You can find more information about Bootstrap and its available styles in the Bootstrap documentation.

Importation of Required Libraries

In this section, we'll cover the further preparation steps that are needed to get our Flask application up and running. Specifically, we'll go over how to import the necessary modules and libraries, and how to instantiate our Flask app and database.

Imports

To get started, we'll need to import the modules and libraries that we'll be using in our application. Here's an example of the imports that we might need:

from flask import Flask, render_template, request, redirect, url_for, flash, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, logout_user, current_user, login_required

In this example, we've imported the following modules and libraries:

  • Flask: the main Flask library

  • render_template: a Flask function that allows us to render HTML templates

  • request: a Flask object that represents the incoming request

  • redirect: a Flask function that allows us to redirect the user to a different URL

  • url_for: a Flask function that generates a URL for a given endpoint

  • flash: a Flask function that allows us to display messages to the user

  • Jsonify: a Flask function that allows us to convert a Python object to a JSON response

  • SQLAlchemy: a library for working with databases in Flask

  • LoginManager: a Flask-Login class that manages the user login and logout process

  • UserMixin: a Flask-Login class that provides default implementations for the methods that Flask-Login expects user objects to have

  • login_user: a Flask-Login function that logs in a user

  • logout_user: a Flask-Login function that logs out a user

  • current_user: a Flask-Login object that represents the currently logged-in user

  • login_required: a Flask-Login decorator that protects a route from unauthorized access

Instantiation

Next, we'll need to instantiate our Flask app and set up our database. To do this, we'll need to create a new Python file in our project directory named config.py and add the following code:

# Create the Flask app and set the secret key
import os
from flask import Flask

basedir = os.path.dirname(os.path.realpath(__file__))

app = Flask(__name__)

# Set up the database
app.config['SQLALCHEMY_DATABASE_URI'] ='sqlite:///' + os.path.join(basedir, 'my_first_blog.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config["SECRET_KEY"] = 'my_secret_key'

db = SQLAlchemy(app)

# Set up Flask-Login
login_manager = LoginManager()
login_manager.init_app(app)

# Create the database tables
@app.before_first_request
def create_tables():
    db.create_all()

# Run the app
if __name__ == '__main__':
    app.run(debug=True)

In this code, we've done the following:

  • Imported the Operating System

  • Created a Flask app and set the secret key

  • Configured the database URI and instantiated an instance of SQLAlchemy

  • Installed and initialized Flask-Login

  • Created a function that creates the database tables when the app starts up for the first time

  • Started the app in debug mode (so that we can see error messages in the browser)

User Management

In this section, we'll go over how to manage users on our blog website. Specifically, we'll cover how to register a new user and how to log in as an existing user.

To implement user management in our Flask application, we'll need to do the following:

  1. Create a database model for storing user information (such as username, email, and password).

  2. Create a signup form for users to register for our website.

  3. Process the signup form submission and create a new user in the database.

  4. Create a login form for users to log in to our website.

  5. Process the login form submission and Login the user.

Let's go through each of these steps in more detail.

Register a User

To register a user, we have to create a database model for storing user information. Also to store user information in our database, we'll need to create a database model. We can do this using Flask-SQLAlchemy, a library that provides a simple interface for working with databases in Flask.

Here's an example of a database model for a user:

class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password = db.Column(db.String(60), nullable=False)

    def __repr__(self):
        return f"User('{self.username}', '{self.email}')"

In this model, we've defined the following fields:

  • id: a unique integer identifier for each user

  • username: a string field for the user's username

  • email: a string field for the user's email

  • password: a string field for the user's password

Create a signup form for users to register for our website

To allow users to register for our website, we'll need to create a signup form. We can do this by creating an HTML template with a form that includes fields for the user's username, email, and password.

Here's an example of a signup form template:

{% extends "base.html" %}

{% block title %} Signup {% endblock %}

{% block content %}
  <h1>Signup</h1>
  <form method="post" action="/signup">
    <label for="username">Username:</label><br>
    <input type="text" id="username" name="username"><br>
    <label for="email">Email:</label><br>
    <input type="email" id="email" name="email"><br>
    <label for="password">Password:</label><br>
    <input type="password" id="password" name="password"><br><br>
    <input type="submit" value="Submit">
  </form> 
{% endblock %}

In this template, we've created a form with fields for the user's username, email, and password, and a submit button. The form is set to submit a POST request to the "/signup" endpoint.

Process the signup form submission and create a new user in the database.

To process the signup form submission and create a new user in the database, we'll need to create a route in our Flask app that handles the form submission.

Here's an example of how we can do this:

@app.route('/signup', methods=['GET', 'POST'])
def signup():
    if request.method == 'POST':
        # Get the form data
        username = request.form['username']
        email = request.form['email']
        password = request.form['password']

        # Validate the form data
        if not username or not email or not password:
            flash('All fields are required')
            return redirect(url_for('signup'))

        # Check if the user already exists
        user = User.query.filter_by(email=email).first()
        if user:
            flash('Email already in use')
            return redirect(url_for('signup'))

        # Create a new user
        new_user = User(username=username, email=email,          
                        password=generate_password_hash(password))

        db.session.add(new_user)
        db.session.commit()

        # Log in the user
        login_user(new_user)

        # Redirect the user to the protected page
        return redirect(url_for('protected'))

    # Render the signup template if the request method is GET
    return render_template('signup.html')

In this route, we've done the following:

  • Checked if the request method is POST (meaning that the form has been submitted).

  • If the request method is POST, we've gotten the form data (username, email, and password) from the request.

  • Validated the form data to ensure that all fields have been filled out.

  • Checked if the user already exists in the database (using the email field as a unique identifier).

  • If the user doesn't already exist, we've created a new user object and added it to the database.

  • Logged in the user using the login_user function from Flask-Login.

  • Redirected the user to the protected page (which is only accessible to authenticated users).

  • If the request method is GET (meaning that the user has accessed the route directly), we've rendered the signup template.

Login User

To allow users to log in to our website, we'll need to create a login form. We can do this by creating an HTML template with a form that includes fields for the user's email and password.

Here's an example of a login form template:

{% extends "base.html" %}

{% block title %} Login {% endblock %}

{% block content %}
  <h1>Login</h1>
  <form method="post" action="/login">
    <label for="email">Email:</label><br>
    <input type="email" id="email" name="email"><br>
    <label for="password">Password:</label><br>
    <input type="password" id="password" name="password"><br><br>
    <input type="submit" value="Submit">
  </form> 
{% endblock %}

To process the login form submission and Login the user, we'll need to create a route in our Flask app that handles the form submission.

Here's an example of how we can do this:

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # Get the form data
        email = request.form['email']
        password = request.form['password']

        # Validate the form data
        if not email or not password:
            flash('All fields are required')
            return redirect(url_for('login'))

        # Check if the user exists
        user = User.query.filter_by(email=email).first()
        if not user:
            flash('Invalid email or password')
            return redirect(url_for('login'))

        # Check if the password is correct
        if not check_password_hash(user.password, password):
            flash('Invalid email or password')
            return redirect(url_for('login'))

        # Log in the user
        login_user(user)

        # Redirect the user to the protected page
        return redirect(url_for('protected'))

    # Render the login template if the request method is GET
    return render_template('login.html')

In this route, we've done the following:

  • Checked if the request method is POST (meaning that the form has been submitted).

  • If the request method is POST, we've gotten the form data (email and password) from the request.

  • Validated the form data to ensure that both fields have been filled out.

  • Checked if the user exists in the database (using the email field as a unique identifier).

  • If the user exists, we've checked if the password is correct using the check_password_hash function from Flask-Bcrypt.

  • If the password is correct, we've logged in the user using the login_user function from Flask-Login.

  • Redirected the user to the protected page (which is only accessible to authenticated users).

  • If the request method is GET (meaning that the user has accessed the route directly), we've rendered the login template.

Logout the Current User

To allow users to log out of our website, we'll need to create a route that handles the logout process.

Here's an example of how we can do this:

@app.route('/logout')
@login_required
def logout():
    logout_user()
    flash('You have been logged out')
    return redirect(url_for('index'))

In this route, we've done the following:

  • Decorated the route with @login_required, which means that only authenticated users can access it.

  • Called the logout_user function from Flask-Login to Logout the current user.

  • Displayed a flash message to the user to confirm that they have been logged out.

  • Redirected the user to the homepage.

With these steps, we've implemented user logout in our Flask app.

Article Management

Display a Single Article On the Home Page

In this section, we'll go over how to display a single article on the homepage of our blog website.

To implement this feature, we'll need to do the following:

  1. Create a database model for storing article information (such as title, body, and author).

  2. Create a route for displaying a single article.

  3. Query the database for the article using its unique identifier (such as its ID).

  4. Render a template with the article information.

Let's go through each of these steps in more detail.

Create a database model for storing Article information

To store article information in our database, we'll need to create a database model. We can do this using Flask-SQLAlchemy, a library that provides a simple interface for working with databases in Flask.

Here's an example of a database model for an article:

class Article(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    body = db.Column(db.Text, nullable=False)
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    author = db.relationship('User', backref=db.backref('articles', lazy=True))

    def __repr__(self):
        return f"Article('{self.title}', '{self.body}', '{self.author_id}')"

In this model, we've defined the following fields:

  • id: a unique integer identifier for each article

  • title: a string field for the article's title

  • body: a text field for the article's body

  • author_id: an integer field that stores the ID of the user who wrote the article

  • author: a relationship field that links the article to the user who wrote it

Create a route for displaying a single article

To display a single article on the homepage, we'll need to create a route in our Flask app that handles the request.

Here's an example of how we can do this:

@app.route('/article/<int:article_id>')
def article(article_id):
    # Query the database for the article
    article = Article.query.get_or_404(article_id)

    # Render the article template with the article information
    return render_template('article.html', article=article)

In this route, we've done the following:

  • Defined a route parameter for the article ID.

  • Queried the database for the article using its ID (using the get_or_404 function from Flask-SQLAlchemy).

  • Rendered the article.html template with the article information passed as a variable.

Render a template with the article information

To display the article information in our template, we'll need to use Jinja, a templating language that allows us to embed variables and logic in our HTML files.

Here's an example of an article.html template, that displays a single article:

{% extends "base.html" %}

{% block title %} {{ article.title }} {% endblock %}

{% block content %}
  <h1>{{ article.title }}</h1>
  <p>{{ article.body }}</p>
  <p>Written by {{ article.author.username }}</p>
{% endblock %}

In this template, we've done the following:

  • Used the extends directive to inherit the layout from the base.html template.

  • Used the block directive to override the title and content blocks.

  • Used Jinja variables (surrounded by double curly braces) to display the article's title, body, and author's username.

With these steps, we've implemented the ability to display a single article on the homepage of our blog website.

Post a New Article

To allow users to post new articles on our blog website, we'll need to do the following:

  • Create a form for submitting a new article.

  • Create a route for handling the form submission.

  • Validate the form data.

  • Save the new article to the database.

  • Redirect the user to the homepage.

Let's go through each of these steps in more detail.

Create a form for submitting a new article

To create a form for submitting a new article, we'll need to use HTML. Here's an example of how we can do this:

<form method="POST" action="{{ url_for('post_article') }}">
  <label for="title">Title</label>
  <input type="text" name="title" id="title">
  <br>
  <label for="body">Body</label>
  <textarea name="body" id="body"></textarea>
  <br>
  <input type="submit" value="Post">
</form>

In this form, we've done the following:

  • Used the form element to define a form.

  • Used the label and input elements to create fields for the title and body of the article.

  • Used the textarea element to create a field for the body of the article.

  • Used the submit input type to create a submit button.

  • Set the form's method to POST and the action to the URL of the route that will handle the form submission.

Create a route for handling the form submission

To handle the form submission, we'll need to create a route in our Flask app. Here's an example of how we can do this:

@app.route('/post_article', methods=['POST'])
@login_required
def post_article():
    # Get the form data
    title = request.form['title']
    body = request.form['body']

    # Validate the form data
    if not title or not body:
        flash('All fields are required')
        return redirect(url_for('post_article'))

    # Create a new article
    article = Article(title=title, body=body, author=current_user)

    # Add the article to the database
    db.session.add(article)
    db.session.commit()

    # Redirect the user to the homepage
    return redirect(url_for('index'))

In this route, we've done the following:

  • Decorated the route with @login_required, which means that only authenticated users can access it.

  • Checked if the request method is POST (meaning that the form has been submitted).

  • Gotten the form data (title and body) from the request.

  • Validated the form data to ensure that both fields have been filled out.

  • Created a new article using the form data and the currently logged-in user as the author.

  • Added the article to the database and committed the changes.

  • Redirected the user to the homepage.

With these steps, we've implemented the ability to post new articles on our blog website.

User Authorization Using LOGIN-REQUIRED

Edit an Article

To allow authenticated users to edit their articles on our blog website, we'll need to do the following:

  • Create a form for editing an article.

  • Create a route for handling the form submission.

  • Validate the form data.

  • Update the article in the database.

  • Redirect the user to the homepage.

Let's go through each of these steps in more detail.

Create a form for editing an article

To create a form for editing an article, we'll need to use HTML. Here's an example of how we can do this:

<form method="POST" action="{{ url_for('edit_article', article_id=article.id) }}">
  <label for="title">Title</label>
  <input type="text" name="title" id="title" value="{{ article.title }}">
  <br>
  <label for="body">Body</label>
  <textarea name="body" id="body">{{ article.body }}</textarea>
  <br>
  <input type="submit" value="Edit">
</form>

In this form, we've done the following:

  • Used the form element to define a form.

  • Used the label and input elements to create fields for the title and body of the article.

  • Used the textarea element to create a field for the body of the article.

  • Used the submit input type to create a submit button.

  • Set the form's method to POST and the action to the URL of the route that will handle the form submission.

  • Used Jinja variables (surrounded by double curly braces) to set the values of the form fields to the existing title and body of the article.

Create a route for handling the form submission

To handle the form submission, we'll need to create a route in our Flask app. Here's an example of how we can do this:

@app.route('/edit_article/<int:article_id>', methods=['POST'])
@login_required
def edit_article(article_id):
    # Query the database for the article
    article = Article.query.get_or_404(article_id)

    # Check if the current user is the author of the article
    if article.author != current_user:
        abort(403)

    # Get the form data
    title = request.form['title']
    body = request.form['body']

    # Validate the form data
    if not title or not body:
        flash('All fields are required')
        return redirect(url_for('edit_article', article_id=article.id))

    # Update the article
    article.title = title
    article.body = body
    db.session.commit()

    # Redirect the user to the homepage
    return redirect(url_for('index'))

In this route, we've done the following:

  • Defined a route parameter for the article ID.

  • Checked if the current user is the author of the article (using the abort function to return a 403 Forbidden error if the user is not the author).

  • Gotten the form data (title and body) from the request.

  • Validated the form data to ensure that both fields have been filled out.

  • Updated the article with the new title and body.

  • Committed the changes to the database.

Delete an Article

To allow authenticated users to delete their articles on our blog website, we'll need to do the following:

  • Create a link for deleting an article.

  • Create a route for handling the delete request.

  • Check if the current user is the author of the article.

  • Delete the article from the database.

  • Redirect the user to the homepage.

Let's go through each of these steps in more detail.

Create a link for deleting an article

To create a link for deleting an article, we'll need to use HTML. Here's an example of how we can do this:

<a href="{{ url_for('delete_article', article_id=article.id) }}">Delete</a>

In this link, we've done the following:

  • Used the a element to create a link.

  • Set the href attribute to the URL of the route that will handle the delete request.

  • Used a Jinja variable (surrounded by double curly braces) to set the article ID as a query parameter in the URL.

Create a route for handling delete request

To handle the delete request, we'll need to create a route in our Flask app. Here's an example of how we can do this:

@app.route('/delete_article/<int:article_id>')
@login_required
def delete_article(article_id):
    # Query the database for the article
    article = Article.query.get_or_404(article_id)

    # Check if the current user is the author of the article
    if article.author != current_user:
        abort(403)

    # Delete the article from the database
    db.session.delete(article)
    db.session.commit()

    # Redirect the user to the homepage
    return redirect(url_for('index'))

In this route, we've done the following:

  • Defined a route parameter for the article ID.

  • Checked if the current user is the author of the article (using the abort function to return a 403 Forbidden error if the user is not the author).

  • Deleted the article from the database.

  • Committed the changes to the database.

  • Redirected the user to the homepage.

With these steps, we've implemented user authorization for deleting articles on our blog website. Only authenticated users who are the authors of an article can delete it.

Adding Likes And Comments To Your Post

To add likes and comments to our blog website, we'll need to do the following:

  • Create models for likes and comments in the database.

  • Create forms for adding likes and comments.

  • Create routes for handling the form submissions.

  • Update the templates to display the likes and comments.

Let's go through each of these steps in more detail.

Create models for likes and comments in the database

To create models for likes and comments in the database, we can use Flask-SQLAlchemy. Here's an example of how we can do this:

class Like(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    article_id = db.Column(db.Integer, db.ForeignKey('article.id'))

class Comment(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    article_id = db.Column(db.Integer, db.ForeignKey('article.id'))
    body = db.Column(db.Text)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

In these models, we've done the following:

  • Defined Like , a model with a foreign key to the User model and a foreign key to the Article model.

  • Defined Comment, a model with a foreign key to the User model and a foreign key to the Article model, as well as a body field for the comment and created_at, a field for the date and time

Create forms for adding likes and comments

To create forms for adding likes and comments, we'll need to use HTML. Here's an example of how we can do this:

<!-- Like form -->
<form method="POST" action="{{ url_for('like_article', article_id=article.id) }}">
  <input type="submit" value="Like">
</form>

<!-- Comment form -->
<form method="POST" action="{{ url_for('add_comment', article_id=article.id) }}">
  <label for="body">Comment</label>
  <textarea name="body" id="body"></textarea>
  <br>
  <input type="submit" value="Comment">
</form>

In these forms, we've done the following:

  • Used the form element to define a form.

  • Used the input element with the submit type to create a submit button for the like form.

  • Used the label and textarea elements to create a field for the body of the comment.

  • Used the submit input type to create a submit button for the comment form.

  • Set the form's method to POST and the action to the URL of the route that will handle the form submission.

    Create routes for handling the form submissions

To handle the form submissions, we'll need to create routes in our Flask app. Here's an example of how we can do this:

@app.route('/like_article/<int:article_id>', methods=['POST'])
@login_required
def like_article(article_id):
    # Query the database for the article
    article = Article.query.get_or_404(article_id)

    # Create a like object
    like = Like(user=current_user, article=article)
    db.session.add(like)
    db.session.commit()

    # Redirect the user to the article page
    return redirect(url_for('article', article_id=article.id))

@app.route('/add_comment/<int:article_id>', methods=['POST'])
@login_required
def add_comment(article_id):
    # Query the database for the article
    article = Article.query.get_or_404(article_id)

    # Get the form data
    body = request.form['body']

    # Validate the form data
    if not body:
        flash('All fields are required')
        return redirect(url_for('article', article_id=article.id))

    # Create a comment object
    comment = Comment(user=current_user, article=article, body=body)
    db.session.add(comment)
    db.session.commit()

    # Redirect the user to the article page
    return redirect(url_for('article

Update the templates to display the likes and comments

To display the likes and comments on our blog website, we'll need to update our templates. Here's an example of how we can do this:

<!-- Display the likes for an article -->
{% for like in article.likes %}
  <p>{{ like.user.username }} likes this</p>
{% endfor %}

<!-- Display the comments for an article -->
{% for comment in article.comments %}
  <h5>{{ comment.user.username }} says:</h5>
  <p>{{ comment.body }}</p>
{% endfor %}

In these templates, we've done the following:

  • Used a Jinja for loop to iterate over the likes for an article and display the username of each user who liked the article.

  • Used a Jinja for loop to iterate over the comments for an article and display the username and body of each comment.

With these steps, we've added the ability to like and comment on articles on our blog website.

Taste Of the Magic

To get a taste of the magic of our blog website, we'll need to do the following:

Run the development server

To run the development server, we'll need to use the python app.py command. Here's an example of how we can do this:

$ python app.py

This will start the development server and make our website available at http://localhost:5000.

Test the features of the website

To test the features of our website, we can open a web browser and visit http://localhost:5000. We should be able to do the following:

  • Register a new user.

  • Login with an existing user.

  • Post a new article.

  • Edit and delete our own articles.

  • Like and comment on articles.

We can also test the authorization features of our website by trying to perform actions that are restricted to certain users (e.g., deleting an article that we didn't create).

With these steps, we can get a taste of the magic of our blog website and see all of its features in action.

Conclusion

In conclusion, we've built a blog website using Flask, Flask-SQLAlchemy, HTML, CSS, Flask-Login, and Flask message flashing. We've done the following:

  • Set up a project folder and virtual environment.

  • Installed Flask.

  • Created a Flask app and designed basic routes for it.

  • Created models for users, articles, likes, and comments in the database.

  • Created HTML templates for the website using Jinja.

  • Styled the website using CSS and Bootstrap.

  • Implemented user management features such as registration, login, and logout.

  • Implemented article management features such as displaying a single article, posting a new article, and editing and deleting articles.

  • Implemented user authorization using Flask-Login's login_required decorator.

  • Added likes and comments to articles.

  • Tested the features of the website.

We've also learned how to use Flask, Flask-SQLAlchemy, HTML, CSS, Flask-Login, and Flask message flashing to build a functional and user-friendly blog website.

With these steps, we've built a complete blog website and learned how to use these technologies to create web applications.

Credits

The following resources were used in the creation of this blog website:

  1. zichdan_Blog; a project built using these principles for the second-semester exam at AltSchool Africa.

  2. There's a lot more to follow here on hashcode and Twitter for more insights from my tech journey. Also, you can check out my GitHub repository for more documentation.

  3. Flask documentation: flask.palletsprojects.com/en/2.1.x

  4. Flask-SQLAlchemy documentation: flask-sqlalchemy.palletsprojects.com/en/2.4.x

  5. HTML documentation: developer.mozilla.org/en-US/docs/Web/HTML

  6. CSS documentation: developer.mozilla.org/en-US/docs/Web/CSS

  7. Flask-Login documentation: flask-login.readthedocs.io/en/latest

  8. Flask message flashing documentation: flask.palletsprojects.com/en/2.1.x/patterns..

These resources were invaluable in helping us understand how to use these technologies to build a blog website.