Table of contents
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')