How to Build a Blog App with Flask

How to Build a Blog App with Flask

A guide to build a cool blog app using the flask framework

Hello There 🖐

If you are thinking of cool projects to add to your portfolio, a blog app could be one. This blog post will demonstrate how to build a blog app using the Flask framework, and basic front-end technologies. Here's a minute video demo of the blog app to be built.

In this basic blog app, a user, pre and post-authentication should be able to see blog posts from other users but only authenticated users can make posts, edit and delete their posts. The date posted and the post author are automatically displayed, and there's also the blog category feature that helps users filter the content they want to see.

See the Full Code Repository

Learning Objectives

  • Set up and deploy a flask web app locally

  • Create Flask database models

  • Query objects in Flask SQLite database

  • User authentication with flask bcrypt

  • Create functional routes/endpoints

Prerequisites

To replicate this demo, you should have:

  • Basic knowledge of HTML, CSS and python

  • A pc/desktop with python(version 3 and above) installed

  • Any code editor installed. (The context of this article is Vs Code)

  • Experience using the command line Interface

Setting up Flask

Flask is a python-based microweb framework used in developing web applications. Unlike Django, flask does not have a lot of tools and libraries but is quite easy to work with, for small and medium-scale applications.

Create and navigate to a project folder for this project, on the CLI or Vs code terminal, and run the commands below to:

  • Create a virtual environment

  • Activate the virtual environment

  • Install flask and flask sqlalchemy

  • Freeze dependencies into the 'requirements.txt' file

      python -m venv <venv name>
      <venv name>/Scripts/Activate (windows) or source/<venv name>/bin/activate(mac) 
      pip install flask, flask_sqlalchemy
      pip freeze > requirements.txt
    

The 'requirements.txt' file keeps track of all the external packages installed in the course of the project, to enable the project to function properly when opened on another computer. Be sure to run the command after each subsequent instalment, to update it, or copy all the packages from the file in the code repo, and run the command below to install all the packages required for this project at once.

pip install -r requirements.txt

Next, create a file named '.flaskenv' in the project folder, and type in the following settings for your development environment:

FLASK_ENV = DEVELOPMENT
FLASK_DEBUG = 1
FLASK_APP = app.py
# type 'pip install python-dotenv' in the terminal to run this file.

Also create the app.py file, import flask and other packages as seen in the code repo, and the app is set for development.

Adding Frontend / User Interface

For this project, I used a ready html-css template, you can download it, or explore other designs on the site with similar features. You can also write the frontend code if it's your strength.

  • Create a 'templates' folder for all the HTML files. Use template inheritance to avoid repetition on all the pages.

  • Create a 'static' folder for CSS files, images, and javascript if any. The Flask framework has been programmed to locate the files in this destination. Linking for CSS files or images in your templates should take this format:

<link href="{{ url_for('static', filename='<name of file>') }}" rel="stylesheet" type="text/css" />

Defining Database Models

Database models are like table columns in which data from the blog will be stored. Database models are written in classes with fields that suit the type of data expected. For this blog app, we will be creating three different model classes for User data, Blog Articles, and Contact Information. We want to store the primary information of users who register with the app, to enable the user authentication feature.

  • Create a db.py file, import and create an instance of sqlalchemy

  • Create a models.py file, import the sqlalchemy instance from the db.py file, and define your models.

# In the db.py file:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
# In the models.py file:
from db import db
# User model for the database. 
# Stores dynamic information gotten from the register form in the columns defined
class User(db.Model, UserMixin):
    id = db.Column(db.Integer(), primary_key=True)
    firstname = db.Column(db.String(255), nullable=False)
    lastname = db.Column(db.String(255), nullable=False)
    email = db.Column(db.String(255), nullable=False, unique=True)
    password = db.Column(db.String(60), nullable=False)
    posts = db.relationship('Blogpost', backref='author', lazy=True)

    def __repr__(self):
        return f"User <{self.firstname}, {self.lastname}>"

The 'posts' column in the User model is to create a one-to-many database relationship with the model for blog posts because a user can make multiple blog posts, and enable the program display the author for each blog post. See the remaining models in the models.py file.

Creating the Database

After defining all the models and app configurations in the app.py file, then it's time to create the database itself. To do this, open the terminal and follow these steps:

python
from app import app, db
with app.app_context():
# press the tab key, db.create_all()
# Hit enter twice

User Authentication

User authentication is simply a feature that allows users to register their information and log in to the app.

  • Create a forms.py file, and install flask_wtf (a package for creating forms in Flask)

  • Define form parameters for user login and registration. See the forms.py file.

  • Install Flask_bcrypt(for password authentication), and Flask_login(To manage user sessions)

  • In the app.py file, create the 'login', 'register' and 'logout' routes

# register user route
# logic validates password and adds users to the database
@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegForm()
    if form.validate_on_submit():
        hashed_pword = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
        user = User(firstname=form.first_name.data, lastname=form.last_name.data, email=form.email.data, password=hashed_pword)
        db.session.add(user)
        db.session.commit()
        flash('Account has been created successfully!', 'success')
        return redirect(url_for('login'))
    return render_template('register.html', title='Register', form=form)

The Flask_login package also has a 'current_user' function that can be used to customise views for only authenticated users in the HTML template. For instance, to make the 'post' button only visible to an authenticated user (recall that in the app demo, only a logged-in user should be able to make a post), we can wrap the button in an 'if' block:

{% if current_user.is_authenticated %} 
   <div class="welcome">
      <h2>Welcome, {{ current_user.firstname }}! make a <button class="post_btn"><a href="{{ url_for('create_post') }}">Post</a></button></h2>
   </div>
{% endif %}

A guest user would not see any content in that block until logged in.

Creating a blog Post

Next, we have to define a route for creating a blog post. When an authenticated user clicks on the 'post' button above, it should lead to a page with a form that accepts the blog post data and displays the post on submission.

  • Create a WTForm class for the blog post in the forms.py file

  • Import the form class to the app.py and define the create_post route

# create_post route allows an authenticated user to make posts on the blog
@app.route('/post', methods=['GET', 'POST'])
def create_post():
    form = PostForm()
    if form.validate_on_submit():
        new_post = Blogpost(title=form.title.data, category=form.category.data, content=form.content.data, user_id=current_user.id)
        db.session.add(new_post)
        db.session.commit()
        flash('Post created successfully!',  'success')
        return redirect(url_for('index'))
    return render_template('post.html', form=form)

Deleting a Blog Post

The delete function is quite straightforward. Note that users should only be able to delete posts made by them. Use the current_user function as demonstrated earlier to restrict the 'delete' button to posts made by the user.

# enables user delete a post
@app.route('/post/<int:post_id>/delete', methods=['POST', 'GET'])
def del_post(post_id):
    post = Blogpost.query.get_or_404(post_id)
    db.session.delete(post)
    db.session.commit()
    flash('Post deleted!',  'success')
    return redirect(url_for('index'))

Editing a Blog Post

To give the app users a better experience, there should be an option for the post author to edit the post, in case of grammatical errors or wrong information. Just like the delete function, it should be restricted to posts made by the user. Here's an implementation for this feature:

# enables  users edit a post created by them
@app.route('/post/<int:post_id>/update', methods=['GET', 'POST'])
def edit_post(post_id):
    post = Blogpost.query.get_or_404(post_id)
    form = PostForm()
    if form.validate_on_submit():
        post.title = form.title.data
        post.category = form.category.data
        post.content = form.content.data
        db.session.commit()
        flash('Your post has been updated!', 'success')
        return redirect(url_for('post', post_id=post.id))
    form.title.data = post.title
    form.category.data = post.category
    form.content.data = post.content   
    return render_template('post.html', post=post, form=form)

Conclusion

This article covers the implementation of basic CRUD features for the app. There are a lot more features that can be added: profile picture display for post authors, comments and replies on posts etc. Feel free to implement these features, and improve the app UI.

I hope this post was helpful, please share your comments below☺