How to Build a Simple Blog with Flask: A Step-by-Step Tutorial for Beginners

🧱 Part 1: Introduction to Flask

🎯 Goal:

By the end of this part, you’ll have a working Flask setup and a basic folder structure ready for your blog project.


πŸ’‘ What is Flask?

Flask is a lightweight web framework written in Python. It lets you build websites and web applications quickly and easily.

Think of Flask as the brain of your blog β€” it decides what happens when someone visits a page, fills out a form, or clicks a button.

🧠 You already know Python β€” now you’ll use it to build a real web app!


🧰 Step 1: Set Up Your Environment

βœ… What you need:

  • Python 3.8+
  • A code editor (like VS Code)
  • A terminal (Command Prompt, Terminal, or VS Code terminal)

πŸ“¦ Step 2: Install Flask

πŸ”„ Create a Project Folder

  1. Open your terminal.
  2. Navigate to where you want to make the project.
  3. Create and move into a folder:
mkdir flask_blog
cd flask_blog

πŸ§ͺ (Optional) Create a Virtual Environment

This keeps your project’s packages separate from other projects.

python -m venv venv

Activate it:

  • Windows:
venv\Scripts\activate
  • macOS/Linux:
source venv/bin/activate

🟒 You’ll know it’s activated when you see (venv) at the start of your terminal line.


πŸ“₯ Install Flask

pip install Flask

πŸ’Ύ Save your dependencies:

pip freeze > requirements.txt

This creates a file with all the packages your app needs.


πŸ—‚οΈ Step 3: Set Up Your Project Structure

Inside your flask_blog folder, create these:

flask_blog/
β”œβ”€β”€ static/           ← CSS, images, etc.
β”œβ”€β”€ templates/        ← HTML files go here
β”œβ”€β”€ app.py            ← Your main Flask app
└── requirements.txt  ← List of packages used

✍️ Step 4: Create Your First Flask App

In app.py, write this:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def home():
    return "Hello, Flask!"

if __name__ == "__main__":
    app.run(debug=True)

πŸš€ Step 5: Run Your Flask App

In the terminal:

python app.py

You’ll see something like:

 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Open a browser and go to http://127.0.0.1:5000. You should see:

Hello, Flask!

🧹 Bonus: Use Flask’s Development Mode

To make coding easier, turn on debug mode (already included with debug=True). This auto-refreshes the app when you save changes.


🧠 Recap

  • You learned what Flask is.
  • You installed Flask and set up your project.
  • You created your first Flask app and saw it in the browser!

πŸ”œ Coming Up in Part 2…

You’ll set up a database, create your first Post model, and display posts on the homepage.


🧱 Part 2: Setting Up the Flask Project and Database

🎯 Goal:

Set up a database to store blog posts. You’ll also create a Post model with the following fields:

  • Title
  • Author
  • Date
  • Content

By the end of this part, your app will be ready to create and store blog post data!


🧰 Step 1: Install Flask-SQLAlchemy

Flask doesn’t include a database by default, but we can easily add one using Flask-SQLAlchemy, a powerful and beginner-friendly extension.

In your terminal:

pip install Flask-SQLAlchemy

Then update your requirements.txt:

pip freeze > requirements.txt

🧠 SQLAlchemy is a library that helps us talk to a database using Python instead of writing SQL code directly.


πŸ—ƒοΈ Step 2: Configure the Database in app.py

Open app.py and update it to include SQLAlchemy:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

app = Flask(__name__)

# πŸ”§ Database configuration
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db'
db = SQLAlchemy(app)

🧠 sqlite:///blog.db means: “create a SQLite database file called blog.db in this folder.”


🧱 Step 3: Create the Post Model

Still in app.py, below your db = SQLAlchemy(app) line, add this class:

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    date_posted = db.Column(db.DateTime, default=datetime.utcnow)
    author = db.Column(db.String(50), nullable=False)
    content = db.Column(db.Text, nullable=False)

    def __repr__(self):
        return f"Post('{self.title}', '{self.date_posted}')"

Let’s break this down:

  • id: A unique number for each post
  • title: Max 100 characters, cannot be blank
  • date_posted: Automatically set to the current date/time
  • author: Who wrote the post
  • content: The main text of the post

πŸ“¦ Step 4: Create the Database

In your terminal, open Python:

python

Then type the following inside the Python shell:

from app import app, db
with app.app_context():
    db.create_all()
exit()

This creates a blog.db file in your folder.


βœ… Step 5: Check That It Works

Let’s quickly test it by creating a fake blog post (optional):

python

Then:

from app import app, db, Post
post = Post(title="My First Post", author="Jamie", content="This is my first blog post!")
with app.app_context():
    db.session.add(post)
    db.session.commit()
exit()

You just added your first blog post directly into the database! πŸŽ‰


🧠 Recap

  • You installed Flask-SQLAlchemy
  • You set up your database configuration
  • You created a Post model
  • You created your first real database entry

πŸ”œ Coming Up in Part 3…

We’ll build a homepage to show your blog posts and learn how to create and use HTML templates with Flask.


🧱 Part 3: Creating Routes and Templates

🎯 Goal:

  • Display blog posts on a homepage using an HTML template.
  • Learn how Flask connects Python to HTML.
  • Use Jinja2, Flask’s built-in template engine, to loop through posts and show them on the page.

πŸ—‚οΈ Step 1: Create the templates Folder

Flask automatically looks for HTML files in a folder called templates.

In your project directory, make sure you have this structure:

flask_blog/
β”œβ”€β”€ static/
β”œβ”€β”€ templates/
β”‚   └── home.html
β”œβ”€β”€ app.py
└── blog.db

Create a new file: templates/home.html


πŸ’¬ Step 2: Create the Homepage Route

Open app.py and update it to include:

from flask import render_template

@app.route("/")
def home():
    posts = Post.query.order_by(Post.date_posted.desc()).all()
    return render_template("home.html", posts=posts)

What does this do?

  • @app.route("/"): This function runs when someone goes to the homepage (/)
  • Post.query.order_by(...): Gets all posts from the database, newest first
  • render_template(...): Loads an HTML file and gives it the posts to display

🧾 Step 3: Create the Template

In templates/home.html, add this:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>My Blog</title>
</head>
<body>
    <h1>My Blog</h1>

    {% for post in posts %}
        <div style="border:1px solid #ccc; padding:10px; margin:10px 0;">
            <h2>{{ post.title }}</h2>
            <small>By {{ post.author }} on {{ post.date_posted.strftime('%B %d, %Y') }}</small>
            <p>{{ post.content }}</p>
        </div>
    {% else %}
        <p>No posts yet!</p>
    {% endfor %}
</body>
</html>

πŸ” Let’s break this down:

  • {% for post in posts %}: Loops through the list of blog posts
  • {{ post.title }}: Inserts the title from the database
  • {% else %}: Shows β€œNo posts yet!” if the list is empty

🧠 This template uses Jinja2, a special syntax inside HTML for Python-like logic.


πŸ§ͺ Step 4: Test It Out

  1. Run your Flask app:
python app.py
  1. Go to http://127.0.0.1:5000
  2. You should see your sample post(s) from Part 2!

🎨 (Optional) Step 5: Add Some Style

You can add a style.css file inside the static folder later.

We’ll focus on that in another part of the tutorial β€” but feel free to play with font sizes, colors, or layout!


🧠 Recap

  • You learned how to connect a route (/) to a template (home.html)
  • You passed data (blog posts) from Python to HTML
  • You used Jinja2 to loop through and display posts on the page

πŸ”œ Coming Up in Part 4…

We’ll add the ability to create new blog posts using a form, and save them into the database!


🧱 Part 4: Creating New Posts with a Form

🎯 Goal:

  • Add a form page where users can create a new blog post.
  • Save the new post into the database.
  • Redirect users back to the homepage to see their post.

🧰 Step 1: Add a Route for New Posts

Open app.py and add this below the home() route:

from flask import request, redirect, url_for

@app.route("/new", methods=["GET", "POST"])
def new_post():
    if request.method == "POST":
        post_title = request.form["title"]
        post_author = request.form["author"]
        post_content = request.form["content"]

        new_post = Post(title=post_title, author=post_author, content=post_content)
        db.session.add(new_post)
        db.session.commit()
        return redirect(url_for("home"))
    
    return render_template("new_post.html")

Let’s break it down:

  • methods=["GET", "POST"]: Allows the page to both show the form (GET) and handle the form submission (POST).
  • request.form[...]: Gets the values the user typed.
  • db.session.add(...): Adds the post to the database.
  • redirect(url_for("home")): Sends the user back to the homepage.

πŸ“ Step 2: Create new_post.html

In the templates folder, make a new file called new_post.html.

Add this code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>New Post</title>
</head>
<body>
    <h1>Create a New Blog Post</h1>

    <form method="POST">
        <label>Title:</label><br>
        <input type="text" name="title" required><br><br>

        <label>Author:</label><br>
        <input type="text" name="author" required><br><br>

        <label>Content:</label><br>
        <textarea name="content" rows="6" cols="40" required></textarea><br><br>

        <button type="submit">Post</button>
    </form>

    <p><a href="/">Back to home</a></p>
</body>
</html>

πŸ§ͺ Step 3: Test It Out!

  1. Run your app if it’s not running:
python app.py
  1. Visit http://127.0.0.1:5000/new
  2. Fill in a post and submit it.
  3. You should be redirected to the homepage, and your new post will be there!

βž• Bonus: Link to the New Post Page

Add this link at the top of your home.html page just under <h1>My Blog</h1>, so users can easily add posts:

<p><a href="/new">Create New Post</a></p>

🧠 Recap

  • You created a new route (/new) for writing posts
  • You built an HTML form
  • You handled form data and saved it to the database
  • You redirected users after submitting

πŸ”œ Coming Up in Part 5…

We’ll learn how to edit and delete posts so students can fully manage their blog entries.


🧱 Part 5: Editing and Deleting Posts

🎯 Goal:

  • Add Edit and Delete buttons to each blog post.
  • Create pages for editing posts.
  • Handle deleting posts from the database.

✏️ Step 1: Add Edit and Delete Links to home.html

Open templates/home.html and update each post block to include Edit and Delete links.

Replace this part:

<h2>{{ post.title }}</h2>
<small>By {{ post.author }} on {{ post.date_posted.strftime('%B %d, %Y') }}</small>
<p>{{ post.content }}</p>

With this:

<h2>{{ post.title }}</h2>
<small>By {{ post.author }} on {{ post.date_posted.strftime('%B %d, %Y') }}</small>
<p>{{ post.content }}</p>

<a href="/edit/{{ post.id }}">Edit</a> |
<a href="/delete/{{ post.id }}" onclick="return confirm('Are you sure you want to delete this post?');">Delete</a>

🧠 post.id is the unique ID of each blog post, which lets us target specific ones.


🧰 Step 2: Add the Edit Route

Open app.py and add this below your other routes:

@app.route("/edit/<int:id>", methods=["GET", "POST"])
def edit_post(id):
    post = Post.query.get_or_404(id)

    if request.method == "POST":
        post.title = request.form["title"]
        post.author = request.form["author"]
        post.content = request.form["content"]
        db.session.commit()
        return redirect(url_for("home"))
    
    return render_template("edit_post.html", post=post)

πŸ“ Step 3: Create edit_post.html

In your templates/ folder, create a file called edit_post.html and add this:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Edit Post</title>
</head>
<body>
    <h1>Edit Post</h1>

    <form method="POST">
        <label>Title:</label><br>
        <input type="text" name="title" value="{{ post.title }}" required><br><br>

        <label>Author:</label><br>
        <input type="text" name="author" value="{{ post.author }}" required><br><br>

        <label>Content:</label><br>
        <textarea name="content" rows="6" cols="40" required>{{ post.content }}</textarea><br><br>

        <button type="submit">Update</button>
    </form>

    <p><a href="/">Back to home</a></p>
</body>
</html>

🧹 Step 4: Add the Delete Route

Back in app.py, add this below the edit route:

@app.route("/delete/<int:id>")
def delete_post(id):
    post = Post.query.get_or_404(id)
    db.session.delete(post)
    db.session.commit()
    return redirect(url_for("home"))

⚠️ This route deletes a post immediately after the user confirms from the browser popup.


πŸ§ͺ Step 5: Test Everything

  • Visit the homepage.
  • Click Edit on a post β€” change some text and save.
  • Click Delete on a post β€” confirm the popup and check that it disappears.
  • Try deleting a post that doesn’t exist by typing /delete/999 β€” Flask should show a 404 page.

🧠 Recap

  • You added Edit and Delete buttons
  • You created a form to update posts
  • You built a route to delete posts safely
  • Your blog app now supports full CRUD (Create, Read, Update, Delete)!

🎨 Part 6: Adding Some Basic CSS Styling

🎯 Goal:

  • Make the blog look cleaner and more readable.
  • Add consistent fonts, spacing, and colors.
  • Keep everything simple with custom CSS.

🧰 Step 1: Create a Static Folder for CSS

Flask expects static files like CSS or images to go in a folder called static.

πŸ“ Inside your project directory, create a new folder named static, and inside that, create a file called style.css.

/project-folder
β”‚
β”œβ”€β”€ static/
β”‚   └── style.css

🎨 Step 2: Write Some Simple CSS

Open static/style.css and add the following:

body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
    color: #333;
    padding: 20px;
    max-width: 800px;
    margin: auto;
}

h1, h2 {
    color: #444;
}

a {
    color: #0066cc;
    text-decoration: none;
    margin-right: 10px;
}

a:hover {
    text-decoration: underline;
}

form {
    background-color: #fff;
    padding: 15px;
    border-radius: 8px;
    box-shadow: 0 0 5px rgba(0,0,0,0.1);
}

input[type="text"], textarea {
    width: 100%;
    padding: 8px;
    margin-bottom: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
}

button {
    background-color: #007BFF;
    color: white;
    padding: 10px 15px;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}

button:hover {
    background-color: #0056b3;
}

.post {
    background-color: #fff;
    padding: 15px;
    margin-bottom: 20px;
    border-radius: 6px;
    box-shadow: 0 0 4px rgba(0,0,0,0.1);
}

🧩 Step 3: Link the CSS to Your HTML Templates

Open each of your templates (home.html, new_post.html, edit_post.html) and add this <link> in the <head> section:

<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">

For example, your updated home.html head would look like:

<head>
    <meta charset="UTF-8">
    <title>My Blog</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>

βœ… Repeat for the other pages too (new_post.html, edit_post.html).


✨ Step 4: Add a Class to Blog Post Blocks

Now let’s use our .post CSS class. In home.html, wrap each blog post like this:

<div class="post">
    <h2>{{ post.title }}</h2>
    <small>By {{ post.author }} on {{ post.date_posted.strftime('%B %d, %Y') }}</small>
    <p>{{ post.content }}</p>

    <a href="/edit/{{ post.id }}">Edit</a>
    <a href="/delete/{{ post.id }}" onclick="return confirm('Are you sure?');">Delete</a>
</div>

πŸ§ͺ Step 5: Refresh and Check

  1. Restart your Flask server (if needed).
  2. Visit your blog pages in the browser.
  3. Everything should now look cleaner, with nice spacing, fonts, buttons, and post containers.

🧠 Recap

  • You learned how to use Flask’s static folder.
  • You wrote and linked a simple CSS file.
  • You gave your blog a fresh and more professional look!

βœ… Part 7: Review and What’s Next

🎯 Goal:

  • Review what students learned.
  • Provide a checklist to ensure everything works.
  • Suggest extension ideas for further exploration.

πŸ“‹ Review Checklist

Let’s make sure your blog is fully functional. Students should go through this checklist:

πŸ”¨ Functionality

FeatureCheck
Homepage displays all blog postsβœ…
Each post shows title, author, date, and contentβœ…
You can create a new post using a formβœ…
You can edit a post and save changesβœ…
You can delete a post with a confirmationβœ…

πŸ–ΌοΈ Styling

FeatureCheck
CSS is applied to all pagesβœ…
Posts are inside styled containersβœ…
Buttons and links are styled and easy to readβœ…
Font and layout are consistent across pagesβœ…

πŸ” Code Structure

FeatureCheck
app.py contains route logic and database setupβœ…
You are using templates for each page (home.html, new_post.html, edit_post.html)βœ…
Static files (like style.css) are stored in the correct folderβœ…
Code is indented and commented neatlyβœ…

🧠 What You Learned

By building this blog, you learned:

βœ… How to use Flask, a Python web framework
βœ… How to work with HTML templates using Jinja2
βœ… How to create and structure a Flask project
βœ… How to use Flask-SQLAlchemy to manage a database
βœ… How to perform full CRUD operations (Create, Read, Update, Delete)
βœ… How to apply basic CSS styling to your web app


πŸš€ Extension Ideas (Optional)

When students are ready to take the project further, here are some fun ideas:

πŸ› οΈ Functional Extensions

  • βœ… Add a search bar to filter posts by keyword.
  • βœ… Add categories or tags for each post.
  • βœ… Include commenting on posts (maybe using a nested list).
  • βœ… Show the number of words or reading time per post.

πŸ‘©β€πŸŽ¨ Design Extensions

  • 🎨 Add a responsive layout using Bootstrap.
  • 🎨 Add a navigation bar across pages.
  • 🎨 Include images with posts (using file upload or URLs).

πŸ›‘οΈ Advanced Extensions

  • πŸ”’ Add user authentication (login/logout, passwords).
  • πŸ“… Show posts by date or sort them.
  • 🌐 Deploy your blog to the web with Render, PythonAnywhere, or Replit.

🧳 Wrap-Up

Congratulations! You’ve just built a fully functional blog using Flask, from scratch. Whether you’re new to Python or web development, this project shows that you’re capable of building real web apps and solving problems creatively. πŸŽ‰