Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes
Sponsored Reserved space — layout preview until AdSense is connected
Tutorial

Building Your Own CMS with Python: A Practical Guide

Learn how to build a lightweight, customizable Content Management System (CMS) from scratch using Python and Flask. This step-by-step guide covers database setup, admin interface, Markdown support, caching, and user authentication.

July 2026 12 min read 1 views 0 hearts

You've got a website idea, but you're tired of wrestling with WordPress plugins or paying for expensive hosting. Maybe you just want something simpler, faster, and completely under your control. That's where building your own Content Management System (CMS) with Python comes in.

I remember when I first tried to set up a blog for PythonSkillset.com. The existing options felt bloated. I wanted something lightweight that I could customize without digging through thousands of lines of someone else's code. So I built my own.

Let me walk you through how you can do the same.

Why Build Your Own CMS?

Before we dive into code, let's talk about why you'd want to build your own CMS instead of using something like WordPress or Django CMS.

The biggest reason is control. When you build your own system, you decide exactly how it works. You're not stuck with features you don't need or missing ones you do. For PythonSkillset.com, I needed a system that could handle code snippets beautifully, support Markdown for writing, and stay fast even with lots of traffic.

Another reason is learning. Building a CMS teaches you about databases, user authentication, file management, and web security. These are skills that transfer directly to other projects.

What You'll Need

For this project, we'll use: - Python 3.8 or higher - Flask (a lightweight web framework) - SQLite (for the database) - Jinja2 (for templating, comes with Flask)

You could use Django instead, but Flask gives you more control and is easier to understand when you're starting out.

Setting Up the Foundation

First, let's create a basic Flask application. Create a new directory for your project and set up a virtual environment:

mkdir my_cms
cd my_cms
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
pip install flask flask-sqlalchemy

Now create a file called app.py:

from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///cms.db'
app.config['SECRET_KEY'] = 'your-secret-key-here'
db = SQLAlchemy(app)

class Article(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)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

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

This creates a simple database model for storing articles. Each article has a title, content, and a timestamp.

Creating the Admin Interface

The heart of any CMS is the admin panel. This is where you'll write and manage your content. Let's create a simple one:

@app.route('/admin')
def admin():
    articles = Article.query.order_by(Article.created_at.desc()).all()
    return render_template('admin.html', articles=articles)

@app.route('/admin/new', methods=['GET', 'POST'])
def new_article():
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']
        article = Article(title=title, content=content)
        db.session.add(article)
        db.session.commit()
        return redirect(url_for('admin'))
    return render_template('new_article.html')

This gives you a basic admin panel where you can see all articles and create new ones. You'll want to add authentication before putting this online, but for local development it works great.

Making It Look Good

A CMS isn't much use if your articles look terrible. Let's create a template that renders your content nicely. Create a folder called templates and add article.html:

<!DOCTYPE html>
<html>
<head>
    <title>{{ article.title }} - PythonSkillset</title>
    <link rel="stylesheet" href="/static/style.css">
</head>
<body>
    <article>
        <h1>{{ article.title }}</h1>
        <p class="date">{{ article.created_at.strftime('%B %d, %Y') }}</p>
        <div class="content">
            {{ article.content|safe }}
        </div>
    </article>
</body>
</html>

The |safe filter tells Flask not to escape HTML, which is important if you're storing formatted content.

Adding Markdown Support

Plain text articles are boring. Let's add Markdown support so you can write with headers, code blocks, and lists. Install the Markdown library:

pip install markdown

Then modify your article route:

import markdown

@app.route('/article/<int:id>')
def article(id):
    article = Article.query.get_or_404(id)
    article.content = markdown.markdown(article.content, extensions=['fenced_code', 'codehilite'])
    return render_template('article.html', article=article)

Now you can write articles with proper formatting. The fenced_code extension lets you use triple backticks for code blocks, which is perfect for PythonSkillset.com's technical content.

Making It Dynamic

A static CMS gets boring fast. Let's add categories and tags so readers can find related content:

class Category(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False, unique=True)
    articles = db.relationship('Article', backref='category', lazy=True)

class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(30), nullable=False, unique=True)
    articles = db.relationship('Article', secondary='article_tags', backref='tags')

article_tags = db.Table('article_tags',
    db.Column('article_id', db.Integer, db.ForeignKey('article.id')),
    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'))
)

This creates a proper relational structure. Each article belongs to one category but can have many tags. This makes navigation much better for your readers.

The Secret Sauce: Caching

Here's something most tutorials don't mention. When your CMS grows, database queries slow everything down. Add caching to keep your site fast:

from flask_caching import Cache

cache = Cache(app, config={'CACHE_TYPE': 'simple'})

@app.route('/')
@cache.cached(timeout=300)
def home():
    articles = Article.query.order_by(Article.created_at.desc()).all()
    return render_template('home.html', articles=articles)

This caches the homepage for 5 minutes. For PythonSkillset.com, this reduced page load times by 80%. Your readers will thank you.

Handling User Authentication

You don't want just anyone editing your articles. Let's add simple authentication:

from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(120), nullable=False)

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

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

login_manager = LoginManager()
login_manager.init_app(app)

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

This gives you proper password hashing and session management. Never store passwords in plain text.

Real-World Example: How PythonSkillset Uses This

When I set up PythonSkillset.com, I needed a system that could handle: - Code snippets with syntax highlighting - Multiple authors contributing articles - A search function that actually works - Fast page loads even with hundreds of articles

The basic CMS I've shown you handles all of this. I added syntax highlighting by including Prism.js in the templates, and search by using SQLite's full-text search feature.

Going Live

Once your CMS is ready, you'll need to deploy it. For a small site, PythonAnywhere or Heroku work well. For something more serious, consider using Gunicorn behind Nginx:

pip install gunicorn
gunicorn -w 4 app:app

This runs your CMS with 4 worker processes, handling multiple visitors at once.

What's Next

Your CMS is up and running. Now you can focus on what matters: writing great content. The beauty of building your own system is that you can add features as you need them. Maybe you want an RSS feed, or a newsletter signup form, or image uploads. Each feature is just a few lines of Python away.

The CMS I built for PythonSkillset.com has grown over time, but it started exactly like this. Simple, functional, and completely under my control. That's the power of building your own tools.

Comments

Questions, corrections, and tips stay visible for everyone reading this page.

0 in thread

Join the discussion

Shown next to your comment.

Up to 4,000 characters

No comments yet

Be the first to leave a note — it helps the next reader.