Practical Flask Project: A Tutorial for Developing a Personal Blog System

1. Preparation: Install Required Tools and Libraries

Before starting, ensure Python (version 3.6+) is installed. Open your terminal and run the following command to install Flask and related extensions:

pip install flask flask-sqlalchemy flask-login flask-wtf flask-bootstrap
  • Flask: Core web framework
  • Flask-SQLAlchemy: ORM tool for simplified database operations
  • Flask-Login: User authentication management
  • Flask-WTF: Form handling and validation
  • Flask-Bootstrap: Quick UI styling (based on Bootstrap)

2. Project Initialization: Directory Structure Setup

Create a blog project folder and organize files as follows:

blog_project/
├── app.py          # Main application entry
├── config.py       # Configuration file (optional, simplified in this tutorial)
├── models.py       # Database model definitions
├── templates/      # HTML template files
   ├── base.html   # Base template (navigation, footer, etc.)
   ├── index.html  # Homepage
   ├── post_detail.html # Single post page
   └── create_post.html # Create new post page
└── static/         # Static resources (CSS, JS, etc.)

3. Database Design: Define Data Models

In models.py, define database models for users and posts:

from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash

db = SQLAlchemy()  # Initialize SQLAlchemy

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50), unique=True, nullable=False)
    password_hash = db.Column(db.String(128), nullable=False)
    # Relationship: One user can write multiple posts
    posts = db.relationship('Post', backref='author', lazy=True)

    # Encrypt password storage
    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    # Verify password
    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    content = db.Column(db.Text, nullable=False)
    timestamp = db.Column(db.DateTime, default=datetime.utcnow)
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)  # Foreign key to User

4. Application Initialization: Configuration and Core Components

In app.py, initialize the Flask app, configure the database, and set up extensions:

from flask import Flask, render_template, redirect, url_for, request, flash
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField
from wtforms.validators import DataRequired
from models import db, User, Post
from flask_bootstrap import Bootstrap

# Initialize Flask app
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'  # Session encryption (use random string in development)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db'  # SQLite database
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False  # Disable unnecessary tracking

# Initialize extensions
db.init_app(app)
Bootstrap(app)  # Enable Bootstrap for UI styling

# Initialize login manager
login_manager = LoginManager(app)
login_manager.login_view = 'login'  # Redirect here if user is not logged in

# User loader callback (required by Flask-Login)
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

# Create database tables (run once at first execution)
with app.app_context():
    db.create_all()

5. Core Function Implementation: Routes and Views

Implement core blog features: homepage, post details, creating posts, and user login.

1. Homepage: Display All Posts

Add the homepage route to app.py:

@app.route('/')
def index():
    # Query all posts sorted by timestamp in descending order
    posts = Post.query.order_by(Post.timestamp.desc()).all()
    return render_template('index.html', posts=posts)

2. Post Detail Page: Display Single Post

Query and display a single post by ID:

@app.route('/post/<int:post_id>')
def post_detail(post_id):
    post = Post.query.get_or_404(post_id)  # Return 404 if post not found
    return render_template('post_detail.html', post=post)

3. Create Post: Form Submission and Storage

Define a post creation form using Flask-WTF:

# Define post creation form
class PostForm(FlaskForm):
    title = StringField('Title', validators=[DataRequired()])
    content = TextAreaField('Content', validators=[DataRequired()])
    submit = SubmitField('Publish')

@app.route('/create', methods=['GET', 'POST'])
@login_required  # Only accessible to logged-in users
def create_post():
    form = PostForm()
    if form.validate_on_submit():
        # Create new post linked to current user
        post = Post(
            title=form.title.data,
            content=form.content.data,
            author_id=current_user.id  # Author is the current logged-in user
        )
        db.session.add(post)
        db.session.commit()
        flash('Post published successfully!', 'success')
        return redirect(url_for('index'))  # Redirect to homepage after publish
    return render_template('create_post.html', form=form)

4. User Login and Logout

Implement user authentication with Flask-Login:

@app.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:  # If already logged in, redirect to homepage
        return redirect(url_for('index'))

    form = LoginForm()  # Login form definition (see below)
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user and user.check_password(form.password.data):
            login_user(user)  # Log the user in
            return redirect(url_for('index'))
        else:
            flash('Invalid username or password', 'danger')

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

@app.route('/logout')
@login_required
def logout():
    logout_user()  # Log out the user
    flash('You have been logged out successfully', 'info')
    return redirect(url_for('index'))

# LoginForm definition (add to app.py)
class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    password = StringField('Password', validators=[DataRequired()])
    submit = SubmitField('Login')

6. Template Rendering: Bootstrap-Styled Pages

Create templates/base.html as the base template (extends for other pages):

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}Personal Blog{% endblock %}</title>
    <!-- Include Bootstrap CSS -->
    {{ bootstrap.load_css() }}
</head>
<body>
    <!-- Navigation Bar -->
    <nav class="navbar navbar-default">
        <div class="container">
            <a class="navbar-brand" href="{{ url_for('index') }}">My Blog</a>
            <ul class="nav navbar-nav navbar-right">
                <li><a href="{{ url_for('index') }}">Home</a></li>
                {% if current_user.is_authenticated %}
                <li><a href="{{ url_for('create_post') }}">New Post</a></li>
                <li><a href="{{ url_for('logout') }}">Logout</a></li>
                {% else %}
                <li><a href="{{ url_for('login') }}">Login</a></li>
                {% endif %}
            </ul>
        </div>
    </nav>

    <!-- Content Area -->
    <div class="container">
        {% with messages = get_flashed_messages(with_categories=true) %}
            {% if messages %}
                {% for category, message in messages %}
                    <div class="alert alert-{{ category }}">{{ message }}</div>
                {% endfor %}
            {% endif %}
        {% endwith %}
        {% block content %}{% endblock %}
    </div>

    <!-- Footer -->
    <div class="navbar navbar-default navbar-fixed-bottom text-center">
        <div class="container">
            <p class="text-muted">© 2023 Personal Blog System</p>
        </div>
    </div>

    {{ bootstrap.load_js() }}
</body>
</html>

1. Homepage Template (templates/index.html)

{% extends "base.html" %}
{% block title %}Home - My Blog{% endblock %}
{% block content %}
<h2 class="text-center">Latest Posts</h2>
{% for post in posts %}
<div class="panel panel-default">
    <div class="panel-body">
        <h3><a href="{{ url_for('post_detail', post_id=post.id) }}">{{ post.title }}</a></h3>
        <p class="text-muted">By: {{ post.author.username }} | Published: {{ post.timestamp.strftime('%Y-%m-%d %H:%M') }}</p>
        <p>{{ post.content[:100] }}... <a href="{{ url_for('post_detail', post_id=post.id) }}">Read More</a></p>
    </div>
</div>
{% endfor %}
{% endblock %}

2. Post Detail Template (templates/post_detail.html)

{% extends "base.html" %}
{% block title %}{{ post.title }} - My Blog{% endblock %}
{% block content %}
<h2 class="text-center">{{ post.title }}</h2>
<p class="text-muted">By: {{ post.author.username }} | Published: {{ post.timestamp.strftime('%Y-%m-%d %H:%M') }}</p>
<hr>
<div class="well">{{ post.content }}</div>
<a href="{{ url_for('index') }}" class="btn btn-primary">Back to Home</a>
{% endblock %}

3. Create Post Template (templates/create_post.html)

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Create Post - My Blog{% endblock %}
{% block content %}
<div class="col-md-6 col-md-offset-3">
    <h2>Write a New Post</h2>
    {{ wtf.quick_form(form, button_map={'submit': 'primary'}) }}
</div>
{% endblock %}

7. Run and Test

Execute the following command in the terminal to start the blog system:

python app.py

Access http://127.0.0.1:5000 to view the homepage. To publish posts, either register (registration functionality is simplified in this tutorial and can be extended) or directly add users to the database.

8. Summary and Extensions

This tutorial implements core blog features:
- Homepage with post listing
- Single post detail page
- Logged-in users can create posts
- UI styling with Bootstrap

Possible Extensions:
1. Add user registration
2. Implement post editing/deletion
3. Comment system
4. Categories and tags
5. Post search

This project helps you master Flask basics, database operations, user authentication, and template rendering—laying the foundation for more complex web applications.

Xiaoye