Build A Simple Blog App with Flask

Build A Simple Blog App with Flask

Table of contents

No heading

No headings in the article.

This is a beginner-friendly approach for building a blog site that is responsive, with different routes and also observing the Database CRUD operation with user authentication.

Getting Started

Project Preparation

  • Create a project folder

  • Set up a virtual environment

  • Install Flask

App basics (backend)

  • Create a flask App

  • Flask Imports

  • Flask Instantiation

  • Prepare Database

  • Create the Models

  • Initialize Database

  • Create the App Routes

  • User Management

  • Posts/Article Management

Frontend touch

  • Create a Folder for Html templates

  • Create a Static folder for CSS

Flask is a lightweight Python framework that is used for building web applications. It has a variety of useful extendable features and allows developers the freedom to develop applications according to their needs.

In this tutorial, we will go through the following concepts as we build this simple Blog App

Responsive Web Design using HTML, CSS & Jinja

Routing

Database Management with SQLite and SQLAlchemy

Internet Security with Werkzeug

User Authentication & Authorization

Message Flashing

Note: For the purpose of this tutorial, everything happens locally using our device as a local server/host.

Tools

To build this project, the following tools are needed:

Python 3

Visual Studio Code (VS Code as IDE "Integrated Development Environment")

Bash Terminal Shell: could be Git BASH.

Bonus: DB Browser for SQLite, this helps us view what we are supplying to the database as it offers a visual interface.

Some knowledge of HTML, CSS and python is required

Create a Project Folder

It is good practice to have all files and folders properly organized on the system. Therefore we will create and dedicate a folder to our project. This can be done through the file manager and as well through our IDE, VS Code or the terminal. To do this using the VS Code terminal, run the following command:

Note: for this tutorial, the linux syntax is used, please find its shell equivalent if you are using windows.

smart@localhost:$ mkdir Techify

Note: "Techify" represents the name of the project folder and mkdir represents the create folder command.

Next we change directory into our project folder to make sure that everything we do happens in our project folder. To do that, run the following code:

smart@localhost:$ cd Techify

Setting Up our Virtual Environment

"Python virtual environments give you the ability to isolate your Python development projects from your system-installed Python and other Python environments. This gives you full control of your project and makes it easily reproducible." - Freecodecamp.

As a best practice, it is required that a new Virtual Environment be created for every project. But if you are a first-time user, you need to first install Virtual Environment.

To install a Virtual Environment, run the following command:

smart@localhost:/Techify$ pip3 install virtualenv

With the above command, we have installed our virtual environment.

To create a virtual environment for our project, which we would name "env", we run the following command:

smart@localhost:/Techify$ python3 virtualenv env

Next, we activate our virtual environment with the following code:

smart@localhost:/Techify$ source env/bin/activate

With the virtual environment activated, the name of the virtual environment will appear in parentheses() at the beginning of the code in our terminal as shown below:

(env) smart@localhost:/Techify$

Just like we activated our virtual environment when we are done working on the project, its advisable to deactivate the virtual environment by running the code below:

(env) smart@localhost:/Techify$ deactivate

With that, the virtual environment is deactivated and seems very easy and simple.

Install Flask

Note: Pip is the package installer for Python, it is used to install different Python libraries and comes bundled with Python. Therefore it will often be seen in use in our tutorial.

In our activated environment, we run the following command to install Flask:

(env) smart@localhost:/Techify$ pip3 install Flask

If you notice, we have been using pip3 in our commands, it is important to state that if you are using Python3, it is always advisable to use pip3 in order to avoid module errors that might arise as a result of using a different version of Python.

App Basics (Backend)

Now we have our Flask project up and running. Next, we create the Flask app.

Create a Flask App

Now we have to create our Flask app.py. We can do that directly on the Vscode root directory console, but here we will do that using our terminal as seen in the command below:

(env) smart@localhost:/Techify$ touch app.py

With that, we have created our app.py file in the Techify directory.

  • Flask Import

Next, we import the Flask module at the head of our app.py file as can be seen below:

from flask import Flask
  • Flask Instantiation

Next, we need to instantiate our Flask app by adding the code below:

from flask import Flask
app = Flask(__name__)
  • Prepare Database

Since we are building a responsive and interactive Blog app, there is a need for a database where User data and Posts/Articles will be stored. This is where our database comes in and for that we will be using SQLAlchemy. You can read more about SQLAlchemy in Flask Here. But the following snippet will be helpful:

"SQLAlchemy is an SQL toolkit that provides efficient and high-performing database access for relational databases. It provides ways to interact with several database engines such as SQLite, MySQL, and PostgreSQL. It gives you access to the database’s SQL functionalities. It also gives you an Object Relational Mapper (ORM), which allows you to make queries and handle data using simple Python objects and methods. Flask-SQLAlchemy is a Flask extension that makes using SQLAlchemy with Flask easier, providing you tools and methods to interact with your database in your Flask applications through SQLAlchemy."

To be able to use the Flask-SQLAlchemy, we need to install it with the following command in our terminal:/c

(env) smart@localhost:/Techify$ pip3 install Flask-SQLAlchemy

Now that we have installed the Flask-SQLAlchemy, we need to import it to be able to use to do that, we add the import at the head of our app.py file with the code below:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import os

app = Flask(__name__)

At this point, we will configure our SQLite database file path which is part of why we imported the os module in our last code. In the os module, we will use some functions to store the path of the base directory in a variable named base_dir. While we do that, we will also configure SQLAlchemy's URI, add a secret key and turn off modification tracking. All these are contained in the code below:

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

app.config["SQLALCHEMY_DATABASE_URI"]='sqlite:///' + os.path.join(base_dir,'blog.db')
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["SECRET_KEY"] = '026b0eb800ec2934fb5cf2e7'

Now that we have configured SQLAlchemy by setting a database URI and disabling tracking, we will create a database object using the SQLAlchemy class, passing the application instance to connect our Flask application with SQLAlchemy. We will store our database object in a variable called db. That is what we will use to interact with our database.

  • db Instantiation

Now we need to create a database object using the SQLAlchemy class and pass our app instance. With that we connect our Flask app with SQLAlchemy. The code is represented below:

db = SQLAlchemy(app)
db.init_app(app)
  • Create the Models

Now we create models that we serve as table representations in our database. It is in the tables that our user data will be stored. For this tutorial, we will only have two tables/models: User and Blog.

a. User Model

As we move in to create the User model, we need to install another Python module that will enable us to use UserMixin, this module is the Flask-Login. Therefore in our terminal, we will run the following command:

(env) smart@localhost:/Techify$ pip3 install Flask-Login

Next, we will import the UserMixin subclass from the Flask-Login module into our app.py file:

from flask_login import UserMixin

Now we create the User model/class with the code below:

class User(db.Model, UserMixin):
    id = db.Column(db.Integer(), primary_key=True)
    first_name = db.Column(db.String(50), nullable=False)
    last_name = db.Column(db.String(50), nullable=False)
    username = db.Column(db.String(50), nullable=False, unique=True)
    email = db.Column(db.String(100), nullable=False, unique=True)
    password_hash = db.Column(db.Text(), nullable=False)

    def __repr__(self):
        return f"User <{self.username}>"

For a deeper understanding of what is going on here, please check here.

b. Blog Model

Next, we create the Blog Model. In this model, we will have the title of the article, the author's name, the post content and the date it was created. To achieve that we need to first import the Python datetime module with the code below:

from datetime import datetime

Now the Blog Model:

class Blog(db.Model, UserMixin):
    id = db.Column(db.Integer(), primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    post = db.Column(db.Text, nullable=False)
    author = db.Column(db.String(50), nullable=False, default='N/A')
    date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)

c. Message Model

With the Message Model, we intend to get feedback from users with details of the sender and the message itself. The code below represents that:

class Message(db.Model):
     __tablename__ = "messages"
     id = db.Column(db.Integer, primary_key=True)
     first_name = db.Column(db.String(50), nullable=False)
     email = db.Column(db.String(80), nullable=False)
     message = db.Column(db.String, nullable=False)
     priority = db.Column(db.String(20))

     def __repr__(self):
         return f"Message: <{self.title}>"
  • Initialize Database

To Initialize our database, we will go to our terminal and open a python interactive shell by running either of the commands below:

(env) smart@localhost:/Techify$ flask shell

OR

(env) smart@localhost:/Techify$ python3

With that, an interactive python shell will open for us. We then Import the database object, the User, the Message and the Blog models, and then run the db.create_all() function to create the tables that are associated with our models, therefore in our terminal, we will run the following commands:

>>> from app import app
>>> from app import db, User

with app.app_context():
     db.create_all()
  • Create the App Routes

When we route, we map and assign a specific URL to handle the logic in each Flask function. Routing is done in Flask with a decorator represented as route() in this tutorial, we will use @app.route(). We also need to import additional Python modules that will enable us to route successfully:

from flask import Flask, render_template, url_for, request, redirect, flash

Now we will route the different pages.

  • Home

In most cases, the home page is identified with the index route and denoted with '/' in routing. In this route, we will create the index() function that will display all our posts/articles. To create the route, we add the following code:

@app.route('/')
def blog():
    blogs = Blog.query.all()
    return render_template('index.html', blogs=blogs)

With the above code, we query our Blog model in our database and Flask will use blogs=blogs to render our articles in our index.html which is the home page.

  • About

The About page tells visitors about the website and it is rendered using the about.html template. It is represented with the code below:

@app.route('/about')
def about():
    return render_template('about.html')
  • Contact

With the contact page, we collect feedback from our users using a form in our contact.html template. Add the code below:

@app.route('/contact', methods=['GET', 'POST'])
def contact():
    if request.method == 'POST':
        first_name = request.form.get('first_name')
        email = request.form.get('email')
        message = request.form.get('message')

        new_message = Message(first_name=first_name, email=email,
                              message=message)
        db.session.add(new_message)
        db.session.commit()

        flash("Thank you for your feedback.")
        return redirect(url_for('index'))

    return render_template('contact.html')

Notice that we passed two arguments in the method 'GET' and 'POST'. That represents HTTP request methods. The 'GET' method allows us to pull up and display data to the user while the 'POST' method allows a user to add or post data to our database/backend. Read more here.

  • User Management

In managing our users, we need to authenticate them and as well authorize them for what they can do on our blog app. To do that we will go ahead and create the registration and login routes to authenticate users.

Register a User

With this route, a function is created that handles the signup.html template and the form it contains to ensure that the registration details return to the database into the User model (table). The code is below:

@app.route('/signup', methods=['POST','GET'])
def register():
    if request.method == 'POST':
        first_name = request.form.get('first_name')
        last_name = request.form.get('last_name')
        username = request.form.get('username')
        email = request.form.get('email')
        password = request.form.get('password')
        confirm = request.form.get('confirm')

        user = User.query.filter_by(username=username).first()
        if user:
            return redirect(url_for('register'))

        email_exists = User.query.filter_by(email=email).first()
        if email_exists:
            return redirect(url_for('register'))

        password_hash = generate_password_hash(password)

        new_user = User(first_name=first_name, last_name=last_name, username=username, email=email, password_hash=password_hash)
        db.session.add(new_user)
        db.session.commit()

        return redirect(url_for('login'))

    return render_template('signup.html')

With the above code, we are ready with the function and logic to register a user. Next is the login.

  • Login a User

When a User has been successfully registered, we then return the User to the login page. To login the User, the login function will check if the the user's input matches any available username and hashed password on our users model (table) in the database. If this is successful, the user is logged in and redirected to the home page. The code is below:

 @app.route('/login', methods=['POST','GET'])
def login():
    username = request.form.get('username')
    password = request.form.get('password')

    user = User.query.filter_by(username=username).first()

    if user and check_password_hash(user.password_hash, password):
        login_user(user)
        return redirect(url_for('blog'))

    return render_template('login.html')

With the above code, we will authenticate our users and only allow those authenticated to be logged in.

  • Log out a Logged in User

Just like we created a function to log in users, the Flask-Login module has existing easy logout function that logs out a user to revoke his access once he clicks the logout link. The function is seen below:

@app.route('/logout')
def logout():
    logout_user()
    return redirect(url_for('blog'))

With the above code, the user is logged out and returned to the home page or any other page as may be needed.

  • Posts/Article Management

In managing the posts, only logged in users should have the authorization to create, edit and delete posts. Therefore with our html templates and matching Flask functions/routes, we will enforce the authorization.

Read More/Full Article

Posts are not fully displayed in the home page, therefore a visitor needs to click a button to read the full details of the post. The code below performs the function:

 @app.route('/post/<int:id>', methods=['POST', 'GET'])
def read_more(id):
    post = Blog.query.get_or_404(id)
    return render_template('read_more.html', post=post)

Once the read more button is clicked, the function above returns the post in full using the post id.

Create a New Article

As already mentioned above, a User has to be logged in to create an article or make a post. This means that only authenticated users can create a post and have the authorization to edit or delete the post. This feature is implemented by protecting some routes such that a user that is not logged in would not see the route. The code is below:

@app.route('/post', methods=['POST', 'GET'])
@login_required
def post():
    if post.author != current_user.first_name:

        flash('You have to login to edit a Post')

    return redirect(url_for('blog'))


    if request.method == 'POST':
        title = request.form.get('title')
        content = request.form.get('post')
        author = current_user.first_name
        date_posted = datetime.now()
        new_post = Blog(title=title, post=content, author=author, date_posted=date_posted)
        db.session.add(new_post)
        db.session.commit()

        return redirect(url_for('blog'))

    return render_template('create_post.html')