๐ Building a Mini Blog with Flask + SQLite
After learning basic Python syntax and building simple console apps, I wanted to step into real web development using Flask. This project is a minimal blog system where you can write posts, save them into a database, and display them on a clean UI.
Unlike the earlier projects, this one required thinking more about structure โ backend logic, routes, HTML templates, and how data flows between each layer. It was definitely more challenging, but I learned a lot through the process. I hope this project helps anyone who wants to start Flask or backend development.
๐ 1. Project Structure
flask_blog/
โโโ app.py # Flask server
โโโ blog.db # SQLite DB (auto-created)
โโโ templates/
โโโ index.html # Show all posts
โโโ new.html # Create new post
๐ง 2. How It Works
- Flask handles routes: home, new post page, create post
- SQLite stores blog posts
- Templates render posts using Jinja2
- Posts are listed in reverse chronological order
๐ฅ๏ธ 3. Main App (app.py)
from flask import Flask, render_template, request, redirect
import sqlite3
# Initialize the Flask application
app = Flask(__name__)
# ---------- Database Initialization Function ----------
def init_db():
conn = sqlite3.connect("blog.db")
cur = conn.cursor()
cur.execute("""
CREATE TABLE IF NOT EXISTS posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT NOT NULL
)
""")
conn.commit()
conn.close()
# Run DB init on app start
init_db()
# ---------- Home Route ----------
@app.route("/")
def index():
conn = sqlite3.connect("blog.db")
cur = conn.cursor()
cur.execute("SELECT id, title, content FROM posts ORDER BY id DESC")
posts = cur.fetchall()
conn.close()
return render_template("index.html", posts=posts)
# ---------- New Post Page ----------
@app.route("/new")
def new_post():
return render_template("new.html")
# ---------- Post Creation ----------
@app.route("/create", methods=["POST"])
def create_post():
title = request.form["title"]
content = request.form["content"]
conn = sqlite3.connect("blog.db")
cur = conn.cursor()
cur.execute(
"INSERT INTO posts (title, content) VALUES (?, ?)",
(title, content)
)
conn.commit()
conn.close()
return redirect("/")
if __name__ == "__main__":
app.run(debug=True)
๐ผ๏ธ 4. Templates
๐ index.html โ Display All Posts
<!DOCTYPE html>
<html>
<head>
<title>Mini Blog</title>
<style>
body { font-family: sans-serif; padding: 20px; background-color: #f4f7f9; }
.post {
border: 1px solid #ddd;
padding: 15px;
margin-bottom: 15px;
border-radius: 8px;
background-color: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.post h3 { color: #333; margin-top: 0; }
.top {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 2px solid #007bff;
margin-bottom: 20px;
padding-bottom: 10px;
}
.top h1 { margin: 0; }
a { text-decoration: none; color: #007bff; font-weight: bold; }
a:hover { text-decoration: underline; color: #0056b3; }
</style>
</head>
<body>
<div class="top">
<h1>Mini Blog Posts</h1>
<a href="/new">Write New Post →</a>
</div>
{% for post in posts %}
<div class="post">
<h3>{{ post[1] }}</h3>
<p>{{ post[2] }}</p>
</div>
{% endfor %}
{% if not posts %}
<p style="color: #6c757d;">No posts yet. Start writing your first entry!</p>
{% endif %}
</body>
</html>
๐ new.html โ Create a New Post
<!DOCTYPE html>
<html>
<head>
<title>Create New Post</title>
<style>
body { font-family: sans-serif; padding: 20px; background-color: #f4f7f9; }
h1 { color: #333; border-bottom: 2px solid #007bff; padding-bottom: 10px; margin-bottom: 20px; }
form {
display: flex;
flex-direction: column;
gap: 15px;
width: 90%;
max-width: 600px;
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
input, textarea {
padding: 12px;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 16px;
}
textarea { resize: vertical; }
button {
padding: 12px;
background: #28a745;
color: #fff;
border: none;
border-radius: 6px;
font-size: 18px;
cursor: pointer;
transition: background 0.3s;
}
button:hover { background: #1e7e34; }
.back-link { margin-top: 20px; display: block; color: #6c757d; text-decoration: none; }
</style>
</head>
<body>
<h1>Create New Post</h1>
<form action="/create" method="POST">
<input type="text" name="title" placeholder="Post Title" required>
<textarea name="content" rows="10" placeholder="Post Content" required></textarea>
<button type="submit">Save Post</button>
</form>
<a href="/" class="back-link">← Back to Home</a>
</body>
</html>
๐ 5. What I Learned
- Building a full CRUD workflow (Create + Read)
- Working with Flask routes, request handling, and redirects
- Rendering templates using Jinja2
- Designing clean UI with HTML + CSS
- Saving data relationally using SQLite
๐ง 6. Try It Yourself โ Easy Improvements
If you're new to Flask and want to practice a little more, here are some simple improvements you can try. Each task is beginner-friendly and will help you understand the project more deeply.
- Add a delete button โ let users remove a post they donโt need anymore.
- Add timestamps โ save the date when a post is created and show it under the title.
- Clear form after submitting โ reset the text fields once a post is saved.
- Improve the design โ change colors, spacing, or fonts to make the blog look nicer.
-
Separate the CSS โ create a small
style.cssfile instead of keeping the CSS in the HTML.
These are small changes, but each one helps you understand how Flask works step by step. Try one at a time โ youโll be surprised how much you can learn from even tiny improvements!
Top comments (3)
Nice work, Sabin! ๐
Building your own mini-blog with Flask is a solid way to practice routing, templates, and overall app structure.
One small suggestion: when working with SQLite, try using a
withblock for your database connections. It automatically handles opening and closing the connection, which helps avoid issues like unclosed connections or locks later on. It keeps the code cleaner too. โจLooking forward to the next parts! ๐ฅ
Thanks for the suggestion @shahrouzlogs ! I haven't implemented SQLite yet in this initial phase. I plan to introduce SQLite in the next project or the one after that. Thank you!
Totally makes sense. Excited to see the next steps, Sabin ๐๐ฝ