Unix-foundation content management for web apps - WordPress escape toolkit
Project description
dbbasic-content
Unix-foundation content management for web apps
Part of the WordPress escape toolkit. Build on Unix foundations instead of rebuilding everything badly.
Philosophy
"Web development takes forever because we reject the solid foundation and build on sand."
dbbasic-content provides WordPress-like content management built on Unix principles:
- Content stored as JSON files on disk (not MySQL)
- Block-based structure (like Gutenberg)
- Simple filesystem operations (grep, cat, version control)
- Zero database daemon required
- Deploy with rsync/FTP like it's 1995 (but better)
Features
- ContentDB: WordPress-like API (
get_post,get_posts,get_page,get_pages) - Posts vs Pages: Separate blog posts (dated, categorized) from static pages
- Block-based: Paragraph, heading, list, card, code, and more
- WordPress Import: Migrate from wp_posts to JSON blocks
- Unix Gateway: Thin layer over filesystem primitives
- Version Control Friendly: JSON files in git
- Grep-able: Find content with standard Unix tools
- Backward Compatible: Existing
articles/directory still works
Installation
# From PyPI
pip install dbbasic-content
# With WordPress import support
pip install dbbasic-content[wordpress]
# From source (development)
pip install git+https://github.com/askrobots/dbbasic-content.git
Quick Start
Posts (Blog Articles)
from dbbasic_content import ContentDB
# Initialize content database
content = ContentDB('/var/app/content')
# Get a post by slug
post = content.get_post('hello-world', content_type='post')
# Get published posts
posts = content.get_posts(content_type='post', status='published', limit=10)
# Get posts by category
tech_posts = content.get_posts(content_type='post', categories=['Technology'])
# Add a new post
content.create_post(
slug='new-post',
title='My New Post',
content_type='post',
author='john',
blocks=[
{'type': 'paragraph', 'data': {'content': 'Hello world!'}},
{'type': 'heading', 'data': {'level': 2, 'content': 'Subheading'}},
]
)
Pages (Static Content)
# Create a page (no date, categories, or tags)
content.create_page(
slug='about',
title='About Us',
blocks=[
{'type': 'heading', 'data': {'level': 1, 'content': 'About Us'}},
{'type': 'paragraph', 'data': {'content': 'We are building the future...'}},
]
)
# Get a page
about = content.get_page('about')
# Get all pages
pages = content.get_pages(status='published')
# Update a page
content.update_page('about', title='About Our Company')
# Delete a page
content.delete_page('old-page')
Posts vs Pages: What's the Difference?
Following WordPress conventions:
Posts (Blog Articles)
- Have publication dates
- Support categories and tags
- Displayed chronologically (newest first)
- Appear in RSS feeds
- Used for: Blog posts, news articles, announcements
Pages (Static Content)
- No dates (timeless content)
- No categories or tags
- Displayed alphabetically or by menu order
- Don't appear in RSS feeds
- Used for: About, Contact, Documentation, Specs
# Create a blog post
content.create_post(
slug='announcing-v2',
title='Announcing Version 2.0',
content_type='post',
date='2025-10-22',
categories=['Announcements'],
blocks=[...]
)
# Create a documentation page
content.create_page(
slug='api-docs',
title='API Documentation',
blocks=[...] # No date, no categories
)
WordPress Migration
from dbbasic_content import WordPressImporter
# Import from WordPress database
importer = WordPressImporter(
host='localhost',
database='wordpress',
user='root',
password='secret'
)
# Convert to JSON blocks
importer.import_to('/var/app/content')
# Converts:
# - wp_posts → articles/*.json
# - wp_postmeta → block metadata
# - wp_terms → categories/tags
# - wp_comments → comments.tsv
CLI Tools
# Initialize content directory
dbcontent init /var/app/content
# Import from WordPress
dbcontent import wordpress \
--host localhost \
--database wordpress \
--user root \
--password secret \
/var/app/content
# List all posts
dbcontent list /var/app/content
# Get post details
dbcontent show /var/app/content hello-world
# Validate content structure
dbcontent validate /var/app/content
Block Types
Supported block types (like Gutenberg):
paragraph- Rich text contentheading- Headings (h1-h6)list- Ordered/unordered listscard- Styled content boxescard_list- Multiple cardscode- Syntax-highlighted codeimage- Images with captionsquote- Blockquotes
Extensible - add your own block types.
Storage Format
/var/app/content/
├── articles/ # Legacy (backward compatible)
│ └── old-post.json
├── posts/ # Blog posts (dated, categorized)
│ ├── hello-world.json
│ └── tech-post.json
├── pages/ # Static pages (no date/categories)
│ ├── about.json
│ └── contact.json
├── metadata.tsv # Post metadata (searchable)
├── taxonomy.tsv # Categories/tags
└── comments.tsv # Comments (if enabled)
Post Format (Blog Article)
{
"slug": "hello-world",
"title": "Hello World",
"content_type": "post",
"date": "2025-01-15",
"author": "john",
"categories": ["Technology"],
"tags": ["python", "web"],
"blocks": [
{
"type": "paragraph",
"data": {"content": "Hello world!"}
}
]
}
Page Format (Static Content)
{
"slug": "about",
"title": "About Us",
"content_type": "page",
"author": "admin",
"status": "published",
"blocks": [
{
"type": "heading",
"data": {"level": 1, "content": "About Us"}
},
{
"type": "paragraph",
"data": {"content": "We are building..."}
}
]
}
Unix Gateway Pattern
# Flask integration
from flask import Flask, render_template
from dbbasic_content import ContentDB
app = Flask(__name__)
content = ContentDB('/var/app/content')
@app.route('/')
def index():
posts = content.get_posts(content_type='post', status='published', limit=10)
return render_template('index.html', posts=posts)
@app.route('/blog/<slug>/')
def blog_post(slug):
post = content.get_post(slug, content_type='post')
return render_template('post.html', post=post)
@app.route('/<slug>/')
def page(slug):
page = content.get_page(slug)
if page:
return render_template('page.html', page=page)
# Fallback to article for backward compatibility
article = content.get_post(slug, content_type='article')
return render_template('page.html', page=article)
That's it. No database daemon. No migrations. Just files.
Why This Exists
WordPress sites paying $150K-400K/year in hosting costs for what amounts to:
- Reading text files
- Rendering HTML
- Checking if user is logged in
This library proves you can have WordPress-level functionality with:
- ~500 lines of Python (not 500K lines of PHP)
- JSON files (not MySQL with 12+ tables)
- Unix foundations (not reinventing everything)
The Stack
- dbbasic-tsv - TSV file storage (done)
- dbbasic-passwd - Unix-style user management (in progress)
- dbbasic-content - Content management (this library)
- WordPress escape tools - Migration toolkit (next)
Development
# Clone and install
git clone https://github.com/quellhorst/dbbasic-content
cd dbbasic-content
pip install -e .[dev]
# Run tests
pytest
# Run tests with coverage
pytest --cov=dbbasic_content --cov-report=html
License
MIT License
Related Projects
- dbbasic-tsv - TSV file database
- dbbasic-passwd - Unix-style user management
Philosophy
Read more: Web Development's Greatest Tragedy: Rejecting Unix's Solid Foundation
Start at 90% complete, not 0%
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dbbasic_content-0.2.0.tar.gz.
File metadata
- Download URL: dbbasic_content-0.2.0.tar.gz
- Upload date:
- Size: 25.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
00e20bf9d20bf110505ad40e40c1e68ed66fe8bf2ff74c988859bbab066c5ee1
|
|
| MD5 |
3a50eab01e5b994dd6b16c250a690adc
|
|
| BLAKE2b-256 |
5b7eaa6a159542b80289df66f23a83833b40f9bc6a11aae01d1ad0c9fb0277b3
|
File details
Details for the file dbbasic_content-0.2.0-py3-none-any.whl.
File metadata
- Download URL: dbbasic_content-0.2.0-py3-none-any.whl
- Upload date:
- Size: 16.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4e59f6368c34050f9a8689c697c3337acb8b5fb87aed378afd75d9d07e36c8b5
|
|
| MD5 |
2aac4de01ec52bd988b0b442b0532c80
|
|
| BLAKE2b-256 |
81551fd4de28bd603298616ba45560e64e8292de43abae8007acb692f1d8c6f1
|