DEV Community

Olakunle Isreal Demilade
Olakunle Isreal Demilade

Posted on

How to build a Blog App with Flask framework in Python

Flask is a micro web framework written in python. It is classified as a micro framework because it does not require particular tools or libraries. It has no database abstraction layer, form validation, or any other components where pre-existing third-party libraries provide common functions.
However, Flask supports extension that can add application features as if they were implemented in Flask itself. This makes it one of the easiest web framework which can be used in many aspect of web development.
In this article, I will explain how one can use this framework to build a Blog Application.

Prerequisites
This article assumes that the reader has the following:

  1. Python 3.10.6 installed on their local developments machine.
  2. Flask 2.2.2 installed on their local development machine.
  3. A basic knowledge HTML, Bootstrap CSS, Python.

Overview

What is Web Framework?
Web Application Framework or simply Web Framework represent a collection of libraries and modules the enables a web application developer to write applications without having to bother about low-level details such as protocols, thread management etc.

What is Web Flask?
Flask is a web application framework written in Python. It is developed by Armin Ronacher, who leads an international group of python enthusiast named Pocco. Flask is based on Werkzeug WGSI toolkit and Jinja2 template engine. Both are Pocco projects.

WSGI
Web Server Gateway Interface (WSGI) has been adopted as a standard for Python web application development. WSGI is a specification for a universal interface between the web server and the web applications.

Werkzeug
It is a WSGI toolkit, which implements requests, response objects and other utility functions. This enables building a web framework on top of it. The Flask framework uses Werkzeug as one of it bases.

Jinja2
Jinja2 is a popular templating engine for Python. A web templating system combines a template with a certain data source to render dynamic web pages.

Flask - Environment
The first thing to do in these journey is to actualize a virtual environment. To do that one has to open the command line of the computer and run the code below to install the environment.

Sudo apt-get install virtualenv
Enter fullscreen mode Exit fullscreen mode

Once installed the new virtual environment is created in a folder.

    mkdir newproj
    cd newproj
    virtualenv venv
Enter fullscreen mode Exit fullscreen mode

Run the above code to change directory to the new virtual environment. To activate corresponding environment run the code below.

venv\scripts\activate
Enter fullscreen mode Exit fullscreen mode

We are now ready to install Flask in this environment.

 pip install Flask
Enter fullscreen mode Exit fullscreen mode

The above command can be run directly, without virtual environment for system-wide installation.

Flask – Application
The next thing to do is to open our code editor preferably visual studio code, you can use any editor of your choice after doing that the next thing is to create some folders and python and html files as shown below

Directories

From the above picture our main directory is named “ISREAL” which replaced the name of the directory we activated our virtual environment in (newproj). The ‘pycache’ folder and the ‘market.db’ file should not be created manually for now. It will be created automatically when we move on to the database part of this tutorial.
The ‘init.py’ file makes the root directory behave like a python module in which we can import our own local made codes from it, so in the init.py file type the below code

from flask import Flask
app = Flask(__name__)
Enter fullscreen mode Exit fullscreen mode

What the above code does is that it import our flask module in the project. An object of Flask is our WSGI application. The flask constructor takes the name of the current module (name) as argument.

In the route.py file put down the code below

from market import app 

@app.route('/')
@app.route('/home')
def home_page():
    return 'hello World'
Enter fullscreen mode Exit fullscreen mode

What the above code does is that it imported the ‘app’ instance in the init.py file since that name is making the root directory become a python module in which we can import our local code instances from. The route() function of the Flask is a decorator, which tells the application which URL should call the associative function.

• The rule parameter represents URL binding with the function.
• The options is a list of parameters to be forwarded to the underlying Rule object

app.route(rule, options)

In the above code, ‘/’ and ‘/home’ URL is bound with home_page() function. Hence when the homepage of the web server is opened in the browser, the output of this function will be rendered.

And in the run.py file put the below code
from market import app

if __name__ == '__main__':
    app.run(debug=True)
Enter fullscreen mode Exit fullscreen mode

What the above code does is that it imported the ‘app’ instance in the init.py file since that name is making the root directory become a python module in which we can import our local code instances from and also checking if the run.py file has executed directly and finally the run() method of Flask class runs the application on the local development server. Note that the debug is set to true above so has to enable the server to reload itself whenever the code changes.
app.run(host, port, debug, options)
All parameters are optional
Sr.No. Parameters & Description
1 host
Hostname is listened on. Defaults to 127.0.0.1 (localhost). Set to ‘0.0.0.0’ to have a server available externally.
2 port
Defaults to 5000
3 debug
Debug to false. If set to true, provides a debug information
4 options
To be forwarded to underlying Werkzeug server

The above file Python script is executed from the command line when we run the code below

python run.py
Enter fullscreen mode Exit fullscreen mode

Flask – Templates
It is possible to return the output of a function bound to a certain URL in the form of HTML. For instance in the code below, the home_page() function will render ‘Hello World’ with the <h1> tag attached to it

@app.route('/')
@app.route('/home')
def home_page():
    return '<html><h1>hello world<h1><html>'
Enter fullscreen mode Exit fullscreen mode

However, generating HTML content from python code is cumbersome, especially when variable data and python language elements like conditionals or loops need to be put. This would require frequent escaping from HTML.
These is where one can take advantage of Jinja2 template engine, on which Flask is based. Instead of running hardcode HTML from the function , a HTML file can be rendered by the render_template() function just by also importing it from flask as shown in the code below.

from market import app
from flask import render_template

@app.route('/')
@app.route('/home')
def home_page():
    return render_template('home.html')
Enter fullscreen mode Exit fullscreen mode

Flask will try to find the HTML file in the templates folder, In the same folder in which the script is present.

Jinja2
Jinja2 is a python HTML template engine. A template engine is a piece of software that combines HTML documents with data from any data source to produce an HTML formatted file that contain the data. Jinja2 is the template engine used in flask, it is built in flask, so you do not need to download a separate package to use jinja2 with flask, it is installed with flask. Even if you are planning to use a different template engine, jinja2 will still be required in order for Flask to run. Jinja2 offers a lot of options when it comes to adding, manipulating or formatting data in the HTML file. Here are some of what Jinja2 offers in your flask app:
• You can pass any data from your flask app to the HTML template.
• Auto escaping (which is enabled by default).
• Filters.
• Context processors.
• Template inheritance.
The Jinja2 template engine uses the following delimiters for escaping from HTML.
{%...%}: for Statements
{{…}}: for Expressions to print to the template output
{#...#}: for Comments not included in the template output
#...##: for Line Statements
Speaking on the point 2 above involves passing information from the backend of our application to the rendered HTML template.

**
Flask – WTF**
WTF stands for WF Forms that help in providing an interactive user interface. Web applications looks interesting only when they have a nice and convenient user interface with user. In HTML we have tag used for designing interface. It provides various kinds of input to accept such as text, textarea, integer, radio, select, etc. But a big problem with HTML forms is that it is difficult to render the forms elements dynamically. And also there is no way provided by HTML to validate user input.
This is where WTForms come to the rescue. These are the flexible forms with validation and rendering library. With WTForms, you can define the form fields in Python script and then you can render them using an HTML template.
Apart from these cool features, WTForms provides these below-mentioned features also because of which it is very helpful to use this module.
• Provides secure form with CRSF token (Cross-Site Request Forgery)
• Provides integration with WTForms.
• Provides file upload that works with Flask Uploads.
CRSF token
Flask-WTF by default prevent all forms from CRSF attacks. It happens by embedding a token in a hidden element () inside the form. This token is used to check authenticity of the request. So, before flask-wtf can generate a CRSF token, a secret key is added. It is done by adding something like the code below to our init.py file

app.config['SECRET_KEY'] = 'development key'
Enter fullscreen mode Exit fullscreen mode

The string in quote can be anything do we can now originally add a secret key to our blog app now let’s just say I use the code below
app.config['SECRET_KEY'] = 'ggsadfa62eq4gq734grr75'

Now to get started with WTForms you first have to install it using the pip installer by running the command below.

pip install flask-wtf
Enter fullscreen mode Exit fullscreen mode

From these module, you can import a Form class which you can perform all form operations. Some of the standard form fields are:
TextField: Used to represent the text field HTML form element.
BooleanField: Used to represent the checkbox.
IntegerField: Represent text field to display the integer values.
TextAreaField: Represent text area form element.
PasswordField: Used to take the password as form input from user.
SubmitField: It represent the submit button of HTML. It basically replaces the piece of code in HTML.

How to use WTForms?
Now that flask-wtf module has been installed. So, if you want to generate a form using wtforms, you just need to follow these simple steps:
 Import the required form module and form fields like TextAreaField, IntegerField, etc.
 Next step is to create a form model.
 Display the HTML form and validate data submitted through the form, if required.
Now before we move to the implementation of the WTForms in the necessary HTML files in our blog app like the login and signup form, let’s quickly talk about the form validation as mentioned in the point 3 above
WTF-Validations
WTForms already has all built in validators we will need in this project. Here are also some of the WTForms validators:
DataRequired(): This validator is used for the field we want to make sure that the user fills in.
• Length(): It is used to check the minimum and maximum length of the inputted data in a field.
Email(): It is used to validate emails.
EqualTo():It is used to check if two fields contain same data
ValidationError(): It is used to raise an error when the inputted data already exist in a a database etc.
Now that you have fully understand WTForms and its validators lets see how it really work. All the forms needed for this project is going to be inside the forms.py folder. Now let start by creating the Registration form, the first thing to do is to import flask_wtf all the necessary Fields and validators from there corresponding modules as shown below.

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField,IntegerField, HiddenField
from wtforms.validators import Length, EqualTo, Email, DataRequired, ValidationError
Enter fullscreen mode Exit fullscreen mode

The next thing to do is create the registration form by writing the code below

class RegisterForm(FlaskForm):


    username = StringField(label='First Name:', validators=[Length(min=2, max=30), DataRequired()])
    lastname = StringField(label='Last Name:', validators=[Length(min=2, max=30), DataRequired()])
    email_address = StringField(label='Email Address:', validators=[Email(), DataRequired()])
    phone_num = StringField(label='Phone Number:', validators=[Length(min=2, max=30), DataRequired()])
    password1 = PasswordField(label='Password:', validators=[Length(min=6), DataRequired()])
    password2 = PasswordField(label='Confirm Password:', validators=[EqualTo('password1'), DataRequired()])
    submit = SubmitField(label='Sign Up') 

Enter fullscreen mode Exit fullscreen mode

As shown in the code above we created a class and passed the FlaskForm as an argument then by assigning the fields to a variable, The label there is represent the label of the field then we can see that all the validations are passed inside the validators Square bracket “[]” then the length there tells us that the minimum data that can be inputted is 2 while the maximum data that can be inputted is 30 and the dataRequired will make it that field compulsory for the user to fill, As we can see in the password variable the EqualTo validator will make sure that the data inputted in password2 must be equal to the one inputted in password1, So we are done with registration form and now to the login form we do the same thing

class LoginForm(FlaskForm):
    username = StringField(label='Username:', validators=[Length(min=2, max=30), DataRequired()])
    password1 = PasswordField(label='Password:', validators=[Length(min=6), DataRequired()])
    submit = SubmitField(label='Log In')
Enter fullscreen mode Exit fullscreen mode

Follow by the form where the user can create a blog

class WriteBlogs(FlaskForm):
    blog_title =  StringField(label='Blog Tiltle:', validators=[DataRequired()])
    blog_content =  StringField(label='Blog Content:', validators=[DataRequired()])
    submit = SubmitField(label='Upload')


Enter fullscreen mode Exit fullscreen mode

Now we are done with the Form creation. The next thing to do is to create a route for each of the form pages and create the HTML pages.
To start with we need to go to our route.py file and import these forms we created so we can send it from the route to the corresponding HTML templates with the help of Jinja2.
In our route folder lets type the codes below

from flask import render_template, redirect, url_for
from market.forms import RegistrationForm, LoginForm, WriteBlogs
Enter fullscreen mode Exit fullscreen mode

The url_for and redirect above are also some packages that were built with flask. The redirect is a key word used when you want a route to redirect to a page after some action has taken place in the route and the url_for() function is very useful for specific function. The function accepts the name of a function as first argument, and one or more keyword argument each corresponding to the variable part of URL.
The next thing is to create the route for each form. You should write the codes below.

@app.route('/Login', methods=['GET', 'POST'])
def Login_page():
    logform = LoginForm()

    return render_template('login.html', form=logform) 

Enter fullscreen mode Exit fullscreen mode
@app.route('/Register')
def Register_page():
    form = RegisterForm()

    return render_template('register.html', form=form) 

Enter fullscreen mode Exit fullscreen mode
@app.route('/writeblog')
def wblogs():
   form = WriteBlogs()

    return render_template('writeblog.html', form=form) 

Enter fullscreen mode Exit fullscreen mode

We can all see from the codes above that in each route we passed the corresponding form to a variable, the variable name can be anything then we are able to send it to the corresponding HTML template which the help of jinja2. This was what I was referring to when I said jinja2 allows us to send information from the backend to the frontend or HTML templates.
Now let design the HTML files. First thing to do is to open our base.html and put code for basic design common to all the pages needed in these project like for example now navigation bar and background color will be common to all the pages so if we put design there then in Flask also with the help of jinja2 we can also call that file in another HTML file and that will make it easier for us to use the base template on any other HTML document rather than rewriting the codes on all HTML documents.

<!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://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
      <title>
          {% block title %}

          {% endblock %}
      </title>
   </head>
   <body>
    <nav class="navbar navbar-expand-md navbar-dark bg-dark">
      <a class="navbar-brand" href="#">Blog App</a>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarNav">
        {% if current_user.is_authenticated %}
        <ul class="navbar-nav mr-auto"> 
            <li class="nav-item">
                <a class="nav-link" href="{{ url_for('blogs_page') }}">Home</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="{{ url_for('wblogs') }}">Write Blog</a>
            </li>

            </ul>
         <ul class="navbar-nav">
         <li class="nav-item">
                <a class="nav-link" href="{{ url_for('about_page') }}">About</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="{{ url_for('logout_page') }}">Log out</a>
            </li>   
         </ul> 
         {% else %}  
        <ul class="navbar-nav mr-auto">

        <li class="nav-item active">
            <a class="nav-link" href="{{ url_for('home_page') }}">Home <span class="sr-only">(current)</span></a>
        </li>
        </ul>
        <ul class="navbar-nav">
            <li class="nav-item">
                <a class="nav-link" href="{{ url_for('Login_page') }}">Login</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="{{ url_for('Register_page') }}">Register</a>
            </li>
        </ul>
        {% endif %}
      </div>
    </nav>
          {% block content %}

      {% endblock %}
      <!-- Future Content here -->



      <!-- Optional JavaScript -->
      <!-- jQuery first, then Popper.js, then Bootstrap JS -->
      <script src='https://kit.fontawesome.com/a076d05399.js'></script>
      <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
      <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
      <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
   </body>
   <style>
      body {
      background-color: #212121;
      color: white
      }
   </style>
</html>

Enter fullscreen mode Exit fullscreen mode

The code above shows the normal HTML codes with bootstrap CSS which I expect u to write on your base.html file and we can also see a jinja2 syntax in the first image above with is stating about the block title, the uses of that block title is that when we call this base.html file in another html file that is where we will put the title of the new page and on the last image there is another one which says block content and this is where the content of the page that we called it in will be.
Now let move to our register.html file. Write the codes below

{% extends 'base.html' %}
{% block title %}
    Register Page
{% endblock %}

{% block content %}
<body class="text-center">
    <div class="container">
        <form  class="form-register" style="color:white">
            {{ form.hidden_tag() }}

            <h1 class="h3 mb-3 font-weight-normal">
                Please Create your Account
            </h1>
            <br>
            {{ form.username.label() }}
            {{ form.username(class="form-control", placeholder="First Name") }}

            {{ form.lastname.label() }}
            {{ form.lastname(class="form-control", placeholder="Last Name") }}

            {{ form.email_address.label() }}
            {{ form.email_address(class="form-control", placeholder="Email Address") }}

            {{ form.phone_num.label() }}
            {{ form.phone_num(class="form-control", placeholder="Phone Number") }}

            {{ form.password1.label() }}
            {{ form.password1(class="form-control", placeholder="Password") }}

            {{ form.password2.label() }}
            {{ form.password2(class="form-control", placeholder="Confirm Password") }}
            <div class="checkbox mb-3">
               <h6>Already have an account?</h6>
               <a class="btn btn-sm btn-secondary" href="{{ url_for('Login_page') }}">Login</a>
            </div>

            <input type="submit" class="btn btn-lg btn-block btn-primary" value="Sign Up">

        </form>
    </div>
</body>
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

In the first image above we call also see a jinja2 syntax that is extending the base.html file in register.html and we can see here that inside the block title jinja2 tag the name there is Register Page then in the block content we can see that that is where the content of the page is, now if we look inside the form tag we can see the hidden tag that will avoid the CRSF. So the above code shows how we access the register form in the HTML template just because we sent it from our backend to the template and they are all enclosed in a jinja2 syntax each field has it label a placeholder and the bootstrap CSS also note that the submit button href is pointing to the function we created for login page.
Now let go to our loggin.html

 {% extends 'base.html' %}
{% block title %}
    Login Page
{% endblock %}

{% block content %}
<body class="text-center">
    <div class="container">
        <form  class="form-signin" style="color:white">
            {{ form.hidden_tag() }}
            <h1 class="h3 mb-3 font-weight-normal">
                Please Login
            </h1>
            <br>
            {{ form.username.label() }}
            {{ form.username(class="form-control", placeholder="Email") }}

            {{ form.password1.label() }}
            {{ form.password1(class="form-control", placeholder="Password") }}

            <br>


            <div class="checkbox mb-3">
               <h6>Do not have an account?</h6>
               <a class="btn btn-sm btn-secondary" href="{{ url_for('Register_page') }}">Register</a>
            </div>

            {{ form.submit(class="btn btn-lg btn-block btn-primary") }}

        </form>
    </div>
</body>
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

Now to our writeblog.html

{% extends 'base.html' %}
{% block title %}
    Home Page
{% endblock %}

{% block content %}
<center>
<div class="col-md-6 col-lg-8"" style="background-color: #343a40!important;
box-shadow: inset 1px -1px 1px #444, inset -1px 1px 1px #444;">
      <form  class="form-signin" style="color:white">
        {{ form.hidden_tag() }}
        <!-- <img class="mb-4" src="https://res.cloudinary.com/jimshapedcoding/image/upload/v1597332609/android-icon-192x192_ove2a7.png" alt=""> -->
        <h1 class="h3 mb-3 font-weight-normal">
            Write A Blog Your Blog
        </h1>
        <br>
        {{ form.blog_title.label() }}
        {{ form.blog_title(class="form-control", placeholder="Blog Title") }}

        {{ form.blog_content.label() }}
        {{ form.blog_content(class="form-control", placeholder="Blog Content") }}
        <input type="hidden"  name="time" id="time" value="{{localtime}}">
        <br>

        {{ form.submit(class="btn btn-lg btn-block btn-primary") }}


    </form>

</div>
</center>
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

The last input tag before the submit button is an hidden input that will tell us the time a blog was created.
Now before moving to the database section of these tutorial let talk about HTTP methods.

Flask – HTTP methods
Http protocol is the foundation of data communication in world wide web. Different methods of data retrieval from specified URL are defined in this protocol.
The following summarizes the different http methods:

  1. GET: Sends data in encrypted form to the server. Most common method.
  2. HEAD: Same has GET but without response body
  3. POST: Used to send HTML form data to sever. Data received by POST method is not cached by server.
  4. PUT: Replaces all current representations of the target resource with the uploaded content.
  5. DELETE: Removes all current representations of the target resource given by a URL.

Flask – Database
As we all know a database is where information are stored. Flask is already built with a database which is SQLite. SQLAlchemy module is what is being used to interact with SQLite database which is readily available in the standard Python library. Data in SQLite are stored in tables and column.
To get started we have to import sqlalchemy from flask and initialize it by adding the code below to our init.py files

from flask_sqlalchemy import SQLAlchemy
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///market.db'
db = SQLAlchemy(app)
Enter fullscreen mode Exit fullscreen mode

Line one shows the importation of sqlalchemy and line two code is to create a Flask application object and set URI for the database to be used then the last line is initialization of the database. Now the next thing to do is to create an object of SQLAlchemy class with the application object as the parameter. Note that the market.db in line 2 above can be replace with ‘anyname.db’.
from market import db
Now in our models.py files where all our database setup will be the first thing to do is to import the db from the init_.py file by typing the code above. Then

 class User(db.Model):
    id = db.Column(db.Integer(), primary_key=True)
    username = db.Column(db.String(length=30), nullable=False, unique=True)
    lastname = db.Column(db.String(length=30), nullable=False, unique=True)
    email_address = db.Column(db.String(length=50), nullable=False, unique=True)
    phone_number = db.Column(db.Integer(), nullable=False)
    password_hash = db.Column(db.String(length=60), nullable=False)


    def __repr__(self):
         f'User {self.username}'
Enter fullscreen mode Exit fullscreen mode

The code above explain how to create an object of SQLAlchemy class with the application object as the parameter. Note the following from the code above:
 id: An integer that represent a primary key. This key will get assigned a unique value by the database entry (that is, a, user registration because the class above represent a table in the database and all the variables represent columns that data will be stored)
 nullable: It means that particular column cannot be empty.
db.sring() states that it is a string column
db.Integer() states that it is an integer column and
unique means that the data in that column must be unique.
Now let create another table that will store user’s blogs.

class Blogs(db.Model):
    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String(length=30), nullable=False)
    blog_title = db.Column(db.String(length=3000), nullable=False)
    blog_content = db.Column(db.String(length=3000), nullable=False)
    time = db.Column(db.String(length=30), nullable=False)

Enter fullscreen mode Exit fullscreen mode

Now to actually set up the database you have to go your command line and make sure you are in the same directory your project is the execute the code below one by one.

python
from market import db
from market.models import User
db.create_all()

exit()

Enter fullscreen mode Exit fullscreen mode

The first line will enter into the python environment, the second line means you are importing the database in your project, the third line means you are importing the User table in the database, db.create_all() means you are creating the User table in the database and after doing this you have successfully created the database with only one table(User). And lastly exit() will help you exit the python environment. You can repeat the process to create the Blogs table as well. Note that the command db.drop_all() can be used to format database.

Flask – Bycrpt
Flask-Bcrypt is a Flask extension that provides bcrypt hashing for application. In other words what bcrypt does is that instead of saving raw password we can convert them to hash to save them in our database so even if someone hack into the database he can’t see the saved user password. To get started we need to install it by running the code below in our command line.
pip install flask_bcrypt
To use the extension we can simply import the class wrapper and pass the flask object back to here. So in the init.py file add the code below

from flask_bcrypt import Bcrypt
bcrypt = Bcrypt(app)

Enter fullscreen mode Exit fullscreen mode

Back To Registration Page
Now let’s really configure what should happen is a user fills the registration form and press the submit button because as it is now nothing will happen if a user should click the button. The first thing to do is add the request method in the form tag in the register.html as shown below
<form method="POST" class="form-signin" style="color:white">
Then to the route we created for register and also add the request method there too (in route.py file) and import the database we have created

from market import db
from market.models import User, Blogs
@app.route('/Register', methods=['GET', 'POST'])

Enter fullscreen mode Exit fullscreen mode

The next thing to do is to write the code below

@app.route('/Register', methods=['GET', 'POST'])
def Register_page():
    form = RegisterForm()
    if form.validate_on_submit():
        user_to_create = User(username=form.username.data,
                              lastname=form.lastname.data,
                              email_address=form.email_address.data,
                              phone_number=form.phone_num.data,
                              password=form.password1.data)
        db.session.add(user_to_create)
        db.session.commit() 
        flash(f'Account successfully created! Now logged in to your acount', category='success')
        return redirect(url_for('Login_page'))

    if form.errors != {}: #If there are not errors from the validations
           for err_msg in form.errors.values():
            flash(f'There was an error with creating a user: {err_msg}', category='danger')
    return render_template('register.html', form=form)

Enter fullscreen mode Exit fullscreen mode

The ‘if’ statement above will work when the user clack the submit button and seeing that will used the User model means we must first import it by adding the code below in our route.py file
from market.models import User, Blogs
Then we can see from the code that the columns we created for the User table is collecting each data from the form the user fills on the registration page. Then the db.session.add () that is taking the user_to_create variable as an argument mean’s we are adding a new data to the User table in the database and db.session.commit() means we are done then the flash there means is taking the message that will be displayed as a flash to the user if the user successfully registered but before we can use flash we need to import it too from flask because it is a flask module
from flask import render_template, redirect, url_for, flash
We also need to add something in our base.html file as shown below right before the block content after the closing of the nav tag

 {% with messages = get_flashed_messages(with_categories=true) %}
    {% if messages %}
       {% for category, message in messages %}
          <div class="alert alert-{{ category }}">
              <button type="button" class="m1-2 mb-1 close" data-dismiss="alert" aria-label="Close">
                  <span aria-hidden="true">&times;</span>
              </button>
              {{ message }}
          </div>
       {% endfor %}
    {% endif %}
  {% endwith %}
Enter fullscreen mode Exit fullscreen mode

The last ‘if’ statement is checking is there is any error in validation. We can add validation from our form.py file as shown below.

class RegisterForm(FlaskForm):
    def validate_username(self, username_to_check):
        user = User.query.filter_by(username=username_to_check.data).first()
        if user:
            raise ValidationError('Username already exists! Please try a different username')

    def validate_email_address(self, email_address_to_check):
        email_address = User.query.filter_by(email_address=email_address_to_check.data).first()
        if email_address:
            raise ValidationError('Email Address already exists! Please try a different email address')

    def validate_phone_num(self, phone_num_to_check):
        number = User.query.filter_by(phone_number=phone_num_to_check.data).first()
        if number:
            raise ValidationError('Phone number already used Please try using a different phone number ')        

    username = StringField(label='First Name:', validators=[Length(min=2, max=30), DataRequired()])

Enter fullscreen mode Exit fullscreen mode

As shown above what the code will do is to check is there is any information the user input is already existing in the User table database then if yes it will raise a validationError (explained earlier at the beginning of this tutorial).
Now to actually save the password bcryptically in our database we need to state that in the User model in our model.py file.

 from market import bcrypt
 class User(db.Model):
    id = db.Column(db.Integer(), primary_key=True)
    username = db.Column(db.String(length=30), nullable=False, unique=True)
    lastname = db.Column(db.String(length=30), nullable=False, unique=True)
    email_address = db.Column(db.String(length=50), nullable=False, unique=True)
    phone_number = db.Column(db.Integer(), nullable=False)
    password_hash = db.Column(db.String(length=60), nullable=False)

     @property
    def password(self):
        return self.password

    @password.setter
    def password(self, plain_text_password):
        self.password_hash = bcrypt.generate_password_hash(plain_text_password).decode('utf-8')

    def check_password_correction(self, attempted_password):
        return bcrypt.check_password_hash(self.password_hash, attempted_password)    

    def __repr__(self):
         f'User {self.username}'

Enter fullscreen mode Exit fullscreen mode

As shown above we first of all import bcrypt from our market module. Then code above will actually receive the user password and turn it to bcrypted form in our database.
Now that we are done with registration let talk about login

Flask – Login
Flask-Login provides user management session management for Flask. It handles the common tasks of login in and login out. To get started we need to install it in our command line by running the code below
pip install flask_login
The next thing to do is to create a login manager class for the application in our init.py file as shown below

 from flask_login import LoginManager
login_manager = LoginManager(app)
Enter fullscreen mode Exit fullscreen mode

Now the next thing to do is to provide a user_loader() function callback. The callback is used to reload the user inject from the user ID stored in the session. Now we need to add the code below to in our models.py file

 from market import db, login_manager

from flask_login import UserMixin

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))
Enter fullscreen mode Exit fullscreen mode

Now let’s talk about Flask-session

Sessions
The main use of session in these tutorial is to enable each client have their own session where their own data will be stored in their session when they are logged in.
Now let structure our login system. The first thing to do is to add the type of request method in the form created for login page just like we did for register page
<form method="POST" class="form-signin" style="color:white">
Then the next thing to do is to import the necessary things as shown below in route.py file

from flask import render_template,  redirect, url_for, flash, request, session
from flask_login import login_user, logout_user
import time
Enter fullscreen mode Exit fullscreen mode

We imported time because we are going to need it to save the time someone post a blog, now to our login route.

@app.route('/Login', methods=['GET', 'POST'])
def Login_page():
    logform = LoginForm()  
    if logform.validate_on_submit():
        attempted_user = User.query.filter_by(email_address=logform.username.data).first()
        me = logform.username.data
        session["username"] = me
        if attempted_user and attempted_user.check_password_correction(
                attempted_password=logform.password1.data
        ):
            login_user(attempted_user)
            flash(f'Success! You are logged in as: {attempted_user.username}', category='success')
            return redirect(url_for('blogs_page'))
        else:
            flash('Username and password are not match! Please try again', category='danger')
    return render_template('login.html', form=logform)
Enter fullscreen mode Exit fullscreen mode

What the above code first do is to filter through the User table to check if the information provided as username exist in the database then it saves the username in a session then it also check if the password provide is the same as what is in the database then if it is, It will log the user in and redirect the page to the blogs page using the login_user() and also flashes a success message to the logged in user or it flashes a danger message if information provided does not exist. Now we are done with the login functionalities of our project.
Now let’s setup the route for writing blogs

 @app.route('/writeblog', methods=['GET', 'POST'] )
def wblogs():
    localtime = time.asctime( time.localtime(time.time()) )
    form = WriteBlogs()
    if "username" in session:
            oruko = session["username"]
            surname = User.query.filter_by(email_address=oruko).first()
            surn = surname.username
            last = surname.lastname
            realname = surn +' '+last

    if form.validate_on_submit():

        blog_to_create = Blogs(name=realname,
                               blog_title=form.blog_title.data,
                               blog_content=form.blog_content.data,
                               time = localtime)

        db.session.add(blog_to_create)
        db.session.commit()
        return redirect(url_for('blogs_page'))
    return render_template('writeblog.html', localtime=localtime, form = form)
Enter fullscreen mode Exit fullscreen mode

We can see from the above code using the time module python and also retrieving the data of the logged in user in the session. With the session data the code will get the logged in user’s full name and lastly the code saves the data to the Blogs table in our database including the time the blog was created then redirect the page to the blogs page likewise called the home page.
Now for a user to log out the code below is responsible

 @app.route('/logout')
def logout_page():
    logout_user()
    flash("You have been logged out!", category='info')
    return redirect(url_for("home_page"))
Enter fullscreen mode Exit fullscreen mode

We just need to create a route for it and then use the logout_user() function.
Now let configure the homepage route and every other necessary route, but before that we need to import our database
from market.models import User, Blogs
Then

 @app.route('/')
@app.route('/home')
def home_page():
    blogs = Blogs.query.all()
    return render_template('home.html', blogs = blogs)
Enter fullscreen mode Exit fullscreen mode

The code is making the blogs available to the home.html folder so the home.html file should me should be

 {% extends 'base.html' %}
{% block title %}
    Home Page
{% endblock %}

{% block content %}
<center>
<div class="col-md-6 col-lg-8"" style="background-color: #343a40!important;
box-shadow: inset 1px -1px 1px #444, inset -1px 1px 1px #444;">
{% for blo in blogs %}

<p> <span style="font-size: 4ch;margin-right:10px;margin-bottom:0;">{{ blo.name }}</span>  on {{ blo.time }}</p>
<p> <span style="font-size: 4ch;">{{ blo.blog_title }}</span></p>
<p><span  style="margin-top:0">{{ blo.blog_content }}</span></p>


{% endfor %}   
</div>
</center>
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

But we should know that the homepage for logged in user should be different from what a visitor should see so here is the route for a logged in user homepage

@app.route('/signedblog', methods=['GET', 'POST'])
def blogs_page():
    blogs = Blogs.query.all()
    if "username" in session:
            oruko = session["username"]
            surname = User.query.filter_by(email_address=oruko).first()
            surn = surname.username
            last = surname.lastname
            realname = surn +' '+last
    return render_template('signedblog.html',realname=realname, blog=blogs)
Enter fullscreen mode Exit fullscreen mode

The above code still using the data stored in the session of the current user to be able to send it to the HTML template rendered so has to know the particular blog created by the particular user as shown below I the signedblog.html file

 {% extends 'base.html' %}
{% block title %}
    Home Page
{% endblock %}

{% block content %}

<center>
    <a class="btn btn-lg btn-block btn-primary" href="{{ url_for('wblogs') }}">Clich here to write a Blog</a>
<div class="col-md-6 col-lg-8"  style="background-color: #343a40!important;
box-shadow: inset 1px -1px 1px #444, inset -1px 1px 1px #444;">
{% for blo in blog %}
{% if blo.name == realname %}
    <p> <span style="font-size: 4ch;margin-right:10px;padding:0px;" >{{ blo.name }}</span>  on {{ blo.time }}</p>
    <p> <span style="font-size: 4ch;">{{ blo.blog_title }}</span></p>
    <p>{{ blo.blog_content }}</p>
    <a href="/editblog/{{blo.id}}/" class="btn btn-primary">Edit Blog</a>
    <a href="/deleteblog/{{blo.id}}/" class="btn btn-danger">Delete Blog</a>
{% else %}    
<p> <span style="font-size: 4ch;margin-right:10px;padding:0px;" >{{ blo.name }}</span>  on {{ blo.time }}</p>
<p> <span style="font-size: 4ch;">{{ blo.blog_title }}</span></p>
<p>{{ blo.blog_content }}</p>
{% endif %}
{% endfor %}   
</div>

</center>
{% endblock %}
<style>
    #bod {
    background-color:#343a40!important;
    /* color: white */
    }
 </style>
Enter fullscreen mode Exit fullscreen mode

The above code shows that a blog created by a logged in user has the functionality to edit and delete the blog. Now to delete the blog we can see that the link is sending the logged in user id in the database to a route that is shown below

@app.route('/deleteblog/<int:id>/', methods=['GET', 'POST'])
def delete(id):
    blog_to_delete = Blogs.query.get_or_404(id)

    db.session.delete(blog_to_delete)
    db.session.commit()
    return redirect(url_for('blogs_page'))
Enter fullscreen mode Exit fullscreen mode

Now to delete it the function takes the id has an argument and search the Blogs table with the same id the delete it by using the db.session.delete().
As for the edit route the code below is responsible

 @app.route('/editblog/<int:id>/', methods=['GET', 'POST'])    
def edit(id):
    blog_to_update = Blogs.query.get_or_404(id)       
    if request.method == "POST":


        blog_to_update.blog_title = request.form.get('blog_title')
        blog_to_update.blog_content = request.form.get('blog_content')

        db.session.commit()
        return redirect(url_for('blogs_page'))
    return render_template('editblog.html', blo =blog_to_update) 
Enter fullscreen mode Exit fullscreen mode

We can see that the route is rendering the below HTML template which is our editblog.html file

 {% extends 'base.html' %}
{% block title %}
    Home Page
{% endblock %}

{% block content %}
<center>
<div class="col-md-6 col-lg-8"" style="background-color: #343a40!important;
box-shadow: inset 1px -1px 1px #444, inset -1px 1px 1px #444;">
<form action="/editblog/{{blo.id}}/" method="POST">
    <label for="Blog Title">Blog Title</label>
    <input id="blog_title" class="form-control" value="{{blo.blog_title}}" name="blog_title">
    <label for="Blog Content">Blog Content</label>
    <input id="blog_content" class="form-control" value="{{blo.blog_content}}" name="blog_content">
    <input type="submit" class="btn btn-outline btn-success" value="Save" >
</form>
</div>
</center>

{% endblock %}
Enter fullscreen mode Exit fullscreen mode

The form in the above code takes an action pointing to the edit route. However this is also giving the html access to all information in that route then it presented the blog information associated with that id the route is carrying in an editable input field then after making changes the user can click save

@app.route('/deleteblog/<int:id>/', methods=['GET', 'POST'])
def delete(id):
    blog_to_delete = Blogs.query.get_or_404(id)

    db.session.delete(blog_to_delete)
    db.session.commit()
    return redirect(url_for('blogs_page'))
Enter fullscreen mode Exit fullscreen mode

Now to delete it the function takes the id has an argument and search the Blogs table with the same id the delete it by using the db.session.delete().
As for the edit route the code below is responsible

 @app.route('/editblog/<int:id>/', methods=['GET', 'POST'])    
def edit(id):
    blog_to_update = Blogs.query.get_or_404(id)       
    if request.method == "POST":


        blog_to_update.blog_title = request.form.get('blog_title')
        blog_to_update.blog_content = request.form.get('blog_content')

        db.session.commit()
        return redirect(url_for('blogs_page'))
    return render_template('editblog.html', blo =blog_to_update) 
Enter fullscreen mode Exit fullscreen mode

After clicking the save button the function searches through the Blog table in the database with the id provided from the route then gets the data changes from the form in the editblog.html then save it only instead of adding a new data in the Bogs database.
Lastly in other to document all the various Flask libraries and modules you should run the code below to create the requirement file which automatically adds up all extension
pip freeze > requirements. txt

I believe starting from the beginning of this tutorial to this point is understandable to you. Hence the ABOUT me page should be a small thing for you to do.
GOODLUCK!!

Conclusion
I believe this article has help you understanding the basics of flask and how to use it to build Web Sites and Blog Applications through the mini Blog App project we did in these tutorial. To the newbies in tech I welcome you to tech world and if you are not a new newbie MORE WINS.

Credits
Tutorial Point Docs
Intro to Flask

Top comments (1)

Collapse
 
_chizim_ profile image
Great Okonkwor Chizimuwa

Boss you're so Informative Walahi.thanks allot.
I'm a beginner web developer using flask framework and this your info has helped me allot.