Getting started with Flask - building a simple  blog app

Getting started with Flask - building a simple blog app

Introduction

Flask is a lightweight Python web framework that provides useful tools and features for creating web applications. It is a microframework that does not include an ORM (Object Relational Mapper) or such features. Flask is easy to get started with and is a good choice for small applications and prototypes.

In this article, you will learn how to build a blog application with a simple crud application using Flask. We will call our blog app "Bloggur", noticed what I did there? Okay, let's dive in.

Prerequisite

Some familiarity with basic Python

Flask

Virtual env

At the end of this article, you will learn how to:

  • Set up a Flask project.

  • Create and model your database

  • Creating the routes

  • Add templates

  • Implement Authentication

  • Secure your routes

Set up the project

To set up the project, we need to set up a virtual environment. I prefer to use virtualenvwrapper - it always you to manage and switch between your virtual environments with one line of code. To get started with virtualenvwrapper, you will have to install it using pip

pip install virtualenvwrapper

Once mkvirtualenvwrapper has been installed, you would have to create a virtual environment. To create a virtual environment, run the following code on your terminal

mkvirtualenv bloggur

To activate the bloggur env you just created, do this:

workon bloggur

This would activate the bloggur environment with 'bloggur' as the prefix on your current working directory.

We would then have to create our folder structure so we can organize our blog app. The project directory will contain:

  • bloggur/, a folder containing your application code and files.

  • templates/, a directory containing html templates.

  • static/, a directory containing static files like css

  • requirements.txt, Installation files telling Python how to install your project.

By the end, your project layout will look like this:


├── bloggur/
│   ├── app.py
│   ├── templates/
│   │   ├── base.html
│   │   ├── about.html
│   │   ├── contact.html
│   │   ├── index.html
│   │   ├── auth/
│   │   │   ├── login.html
│   │   │   └── signup.html
│   │   └── blog/
│   │       ├── create.html
│   │       ├── blog.html
│   │       └── edit_post.html
│   │       └── post_detail.html
│   └── static/
│       └── style
│   │       └── main.html
│   │   └── img/
│   │       ├── bloggur.png
├── bloggur.db/
├── requirments.txt
(bloggur) bloggur>

Now let's install the packages we need using pip

pip install Flask

Creating a Base Application

Now that we have set up our project by creating our virtual environment and our project folders, lets a create an app

Inside your bloggur folder, create a file named app.py. You could open your bloggur folder on vscode if you have it installed or you could use any code editor you prefer.

Inside the app.py file, write the following code:

from flask import Flask
app = Flask(__name__)

Set up the database

In this step, you’ll set up a database to store data, that is, the blog posts for your application. We'll be using a database library called sqlalchemy - a nice API for interacting with databases.

To use sqlalchemy with our blog app, you will need to install it first using pip.

pip install flask-sqlalchemy

Next, you will need to create a sqlalchemy object and use it to connect to your database. You will also need to define your database models, which will determine the structure of your data and how it is stored in the database.

First let's import flask-sqlalchemy and set up our configurations

from flask_sqlalchemy import SQLAlchemy

Now let's set configurations and specify our database name and location

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

For our blog, we'll need the Blog model and the User model because we would need users to sign in much later. Add the following code to your app.py file

class Blog(db.Model):
    __tablename__ = "Blog"
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String, unique=True, nullable=False)
    content = db.Column(db.Text, nullable=False)
    date_published = db.Column(db.DateTime)
    author = db.Column(db.String, default="Iniobong Benson")
    def __repr__(self):
        return f"<Blog {self.id}"

class User(db.Model, UserMixin):
    __tablename__ = "User"
    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(255), nullable=False, unique=True)
    email = db.Column(db.String(255), nullable=False, unique=True)
    password_hash = db.Column(db.Text(), nullable=False)
    def __repr__(self):
        return f"{self.first_name}"

db.create_all()

The db.create_all() command creates the database when the app is run for the first time.

Now let's run our application, once we run this app, our database bloggur.db would be created with two (2) tables - Blog table and User table.

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

set FLASK_APP=app

You need to tell Flask to run it in a development environment

set FLASK_ENV=development

Lastly, run the application using the flask run command

flask run

The bloggur application is now running on your localhost on port 5000, http://127.0.0.1:5000/. Copy the url and paste into your browser.

Creating the routes

In Flask, a "route" is a URL pattern that is mapped to a specific function. You will need to create a route for each of the CRUD operations (create, read, update, delete) and write the corresponding function for each route. To create a route in Flask, you will use the @app.route decorator

Before we dive into this section, lets make some imports that we would be needing. Add these import to the line of your code where you imported Flask

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

These imports would be used to render out data to the HTML frontend.

render_template is used to render a template (usually an HTML file) and return the resulting string to the client.

redirect is used to redirect the client's browser to a different URL.

url_for is used to generate URLs for views (i.e. functions that handle requests) based on the name of the view and any arguments it takes.

request is a global object that represents the current incoming request. It contains information such as the client's IP address, the headers, and the body of the request.

flash is used to store a message in the session that can be retrieved after the client is redirected. This is typically used to send one-time messages such as error messages or success messages.

session is an object that allows you to store data in the client's browser across multiple requests. This is typically used to store data that is specific to a particular user, such as their login status or their shopping cart.

abort is a helper function that generates a response indicating that the request cannot be fulfilled, along with an HTTP status code such as 404 (Not Found) or 403 (Forbidden).

Now that we have imported what we needs, lets get into creating the routes

The home route

@app.route('/', methods=['GET'])
def index():
    posts = Blog.query.all()
    return render_template('index.html', posts=posts)

The blog route

We also need to be able to display a logged in user's post that only them can perform CRUD operations on

@app.route('/blog')
def blog():
    if current_user.is_authenticated:
        blog = Blog.query.filter_by(author=str(current_user)).all()
        return render_template('blog/blog.html', user_posts=blog)

The Post route

Registered users need to be able to create their blog post and also see all the posts they have made

@app.route('/post', methods=['GET', 'POST'])
def post():
    if request.method == "POST":
        title = request.form.get('title')
        content = request.form.get('content')
        date_published = datetime.now()
        author = current_user.first_name
        new_post = Blog(title=title, content=content, date_published=date_published, author=author)
        if new_post is None:
            flash("Error - Your post title or content is empty")
            return redirect(url_for('post'))
        else:
            db.session.add(new_post)
            db.session.commit()
            return redirect(url_for('blog'))
    return render_template('blog/create.html')

This route works with a form on the frontend that has input fields like title and content. The other values are automatically generated when the post is created. The date_published uses the Python DateTime module to store the current date and time when the post is created and the author is populated by the user that is currently logged in.

Now lets create the routes for a single post. Here were would be able to fetch a post using an ID, edit that post, or delete it altogether.

Get Post by ID

@app.route('/post/<int:id>')
def get_post(id):
    post = Blog.query.filter_by(id=id).first()
    other_posts = random.sample(Blog.query.all(), 3)
    if post:
        return render_template('blog/post_detail.html', post=post, other_posts=other_posts)
    else:
        flash('That post no longer exists or was never created')
        return redirect(url_for('index'))

@app.route('/post/<int:id>/edit', methods=['GET', 'POST'])
@login_required
def edit_post(id):
    post = Blog.query.get_or_404(id)
    if post.author != current_user.first_name:
        flash("You don't have permission to edit this post")
        return redirect(url_for('get_post', id=post.id))

        # abort(403, "<h1>You don't have permission to edit this post</h1>")

    if request.method == 'POST':
            title = request.form.get('title')
            content = request.form.get('content')
            post.title = title
            post.content = content
            db.session.commit()
            return redirect(url_for('get_post', id=post.id))
    else:

        return render_template('blog/edit_post.html', post=post)



@app.route('/post/<int:id>/delete', methods=['GET', 'POST'])
@login_required
def delete_post(id):
    post = Blog.query.get_or_404(id)
    if post.author != current_user.first_name:
        abort(403)
    else:

        db.session.delete(post)
        db.session.commit()
        flash('Post deleted successfully')
        return redirect(url_for('blog'))

The About Us route

This route shows the about us page. All we need to do here is render the about us template we would be creating in the next section

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

Contact us route

Here we are going to do some interesting. We want to receive mail every time someone submits a message via the contact form. We'll use SendGrid Email API to send the messages to our personal email address. You could use this link to learn more about the project on GitHub.

To accomplish this, we need to sign up on the SendGrid website and set up an API key. Once you log in, you'll go to the Email API section and select, Integration Guide, and select Python as the language. On the new page, create your API key, copy your API to our env file. On your project folder, create an .env file and put in the following

SENDGRID_API_KEY='YOUR_API_KEY'

Now let's install sendgrid using pip

pip install sendgrid

On your import lists, add the followingSendGrid modules

from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail

On your contact us route, write the following code

@app.route('/contact', methods=['GET', 'POST'])
def contact():
    # using SendGrid's Python Library
# https://github.com/sendgrid/sendgrid-python
    if request.method == 'POST':
        message = Mail(
                from_email='your_sendgrid_email@gmail.com',
                to_emails='targetemail@gmail.com',
                subject=request.form.get('subject'),
                html_content=request.form.get('message'))
        try:
                sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
                response = sg.send(message)
                print(response.status_code)
                print(response.body)
                print(response.headers)
                flash('Message sent successfully', category='success')
        except Exception as e:
                print(e)
    return render_template('contact.html')

Now let's set up our templates so we can see what our blog looks like. We are going to use the Jinja2 template engine to write HTML templates with placeholders for dynamic data.

Conclusion

This article provides a step-by-step guide on how to build a blog application using the Flask microframework in Python. It starts by setting up a virtual environment and creating a project directory and file structure, then goes on to explain how to create a Flask application and connect it to a database using SQLAlchemy. The article also covers how to create routes and templates, implement authentication, and secure routes. By the end of the article, the reader will have a basic understanding of how to build a CRUD blog application using Flask.