Python Flask Blog Application Tutorial

Written by

in

Building a blog with Python and Flask is one of the most rewarding ways to learn web development. In this step‑by‑step tutorial you’ll create a fully functional blog application from scratch, covering everything from environment setup to deployment. Whether you’re a beginner looking for a practical project or an experienced developer wanting a lightweight CMS, this guide provides clear code examples, best‑practice tips, and SEO‑friendly content that will help your site rank higher in search results.

Why Choose Flask for a Blog?

Flask is a micro‑framework that gives you the flexibility to design your application exactly the way you want, without the overhead of large, opinionated frameworks. Its lightweight nature makes it perfect for a blog because you can start small and scale up as your audience grows. Key advantages include:

  • Simple routing: Define URLs with just a decorator.
  • Jinja2 templating: Powerful, Python‑like syntax for dynamic HTML.
  • Extensible ecosystem: Add extensions for databases, authentication, and more.
  • Great documentation: Plenty of tutorials and community support.

Prerequisites

Before diving into code, make sure you have the following tools installed on your development machine:

  • Python 3.9 or newer
  • pip (Python package manager)
  • Git (optional, but useful for version control)
  • A code editor such as VS Code, PyCharm, or Sublime Text

Familiarity with basic Python syntax and HTML will make the tutorial smoother, but each concept is explained in plain language.

Step 1: Setting Up the Project Environment

Create a virtual environment

Isolating your project’s dependencies prevents conflicts with other Python applications. Run the following commands in your terminal:

mkdir flask_blog
cd flask_blog
python -m venv venv
source venv/bin/activate   # On Windows use: venv\Scripts\activate

Install Flask and required extensions

We’ll use Flask for the core framework, Flask‑SQLAlchemy for database handling, and Flask‑Migrate for migrations.

pip install Flask Flask-SQLAlchemy Flask-Migrate

Step 2: Creating the Basic Flask Application

Project structure

Organize your files as shown below. This layout keeps the code clean and makes future extensions easier.

flask_blog/
│
├─ app/
│   ├─ __init__.py
│   ├─ models.py
│   ├─ routes.py
│   └─ templates/
│       ├─ base.html
│       ├─ index.html
│       └─ post.html
│
├─ migrations/
│   └─ (auto‑generated)
│
├─ config.py
└─ run.py

Initialize the Flask app

Create app/__init__.py and configure the core components:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

db = SQLAlchemy()
migrate = Migrate()

def create_app():
    app = Flask(__name__)
    app.config.from_object('config.Config')

    db.init_app(app)
    migrate.init_app(app, db)

    with app.app_context():
        from . import routes, models   # Import routes & models
        return app

Configuration file

Store sensitive settings in config.py. For a simple blog, SQLite works well.

import os

class Config:
    SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key')
    SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL', 'sqlite:///blog.db')
    SQLALCHEMY_TRACK_MODIFICATIONS = False

Step 3: Defining the Database Model

In app/models.py we’ll create a Post model that stores the title, content, and timestamp of each blog entry.

from datetime import datetime
from . import db

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(150), nullable=False)
    slug = db.Column(db.String(150), unique=True, nullable=False)
    content = db.Column(db.Text, nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    def __repr__(self):
        return f'<Post {self.title}>'

    def to_dict(self):
        return {
            'id': self.id,
            'title': self.title,
            'slug': self.slug,
            'content': self.content,
            'created_at': self.created_at.isoformat()
        }

Step 4: Building Routes and Views

Home page – list of posts

The home route fetches all posts from the database and renders them with Jinja2.

from flask import render_template, abort, request, redirect, url_for, flash
from . import db
from .models import Post
from . import create_app

app = create_app()

@app.route('/')
def index():
    posts = Post.query.order_by(Post.created_at.desc()).all()
    return render_template('index.html', posts=posts)

Single post page

@app.route('/post/<slug>')
def post_detail(slug):
    post = Post.query.filter_by(slug=slug).first_or_404()
    return render_template('post.html', post=post)

Creating a new post (CRUD – Create)

For simplicity, we’ll use a basic HTML form. In production you’d protect this route with authentication.

@app.route('/admin/new', methods=['GET', 'POST'])
def new_post():
    if request.method == 'POST':
        title = request.form['title']
        slug = request.form['slug']
        content = request.form['content']
        post = Post(title=title, slug=slug, content=content)
        db.session.add(post)
        db.session.commit()
        flash('Post created successfully!', 'success')
        return redirect(url_for('index'))
    return render_template('new_post.html')

Step 5: Designing Templates with Jinja2

Base layout

All pages inherit from base.html, which includes the navigation bar, SEO meta tags, and a content block.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}My Flask Blog{% endblock %}</title>
    <meta name="description" content="A simple, SEO‑optimized blog built with Python Flask.">
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <nav>
        <a href="{{ url_for('index') }}">Home</a> |
        <a href="{{ url_for('new_post') }}">New Post</a>
    </nav>
    <main>
        {% block content %}{% endblock %}
    </main>
    <footer>
        <p>© {{ datetime.utcnow().year }} My Flask Blog</p>
    </footer>
</body>
</html>

Index page

{% extends "base.html" %}
{% block title %}Home – My Flask Blog{% endblock %}
{% block content %}
    <h2>Latest Posts</h2>
    {% for post in posts %}
        <article>
            <h3><a href="{{ url_for('post_detail', slug=post.slug) }}">{{ post.title }}</a></h3>
            <small>Published on {{ post.created_at.strftime('%B %d, %Y') }}</small>
            <p>{{ post.content[:200] }}...</p>
        </article>
        <hr>
    {% else %}
        <p>No posts yet. Be the first to <a href="{{ url_for('new_post') }}">publish</a>!</p>
    {% endfor %}
{% endblock %}

Single post page

{% extends "base.html" %}
{% block title %}{{ post.title }} – My Flask Blog{% endblock %}
{% block content %}
    <article>
        <h2>{{ post.title }}</h2>
        <small>{{ post.created_at.strftime('%B %d, %Y') }}</small>
        <div>{{ post.content|safe }}</div>
    </article>
    <a href="{{ url_for('index') }}">← Back to Home</a>
{% endblock %}

Step 6: Adding Basic SEO

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *