Skip to main content

dbbasic-content

PyPI version Python versions License: MIT

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 content
  • heading - Headings (h1-h6)
  • list - Ordered/unordered lists
  • card - Styled content boxes
  • card_list - Multiple cards
  • code - Syntax-highlighted code
  • image - Images with captions
  • quote - 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

  1. dbbasic-tsv - TSV file storage (done)
  2. dbbasic-passwd - Unix-style user management (in progress)
  3. dbbasic-content - Content management (this library)
  4. 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

Philosophy

Read more: Web Development's Greatest Tragedy: Rejecting Unix's Solid Foundation


Start at 90% complete, not 0%

Release files for dbbasic-content 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dbbasic-content 0.2.0
File Size Uploaded
dbbasic_content-0.2.0.tar.gz 25.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dbbasic-content 0.2.0
File Interpreter ABI Platform
dbbasic_content-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 41.8 kB

Release files / dbbasic_content-0.2.0.tar.gz

Download URL dbbasic_content-0.2.0.tar.gz
Size 25.8 kB
Tags Source
SHA-256 checksum
How to use checksums
00e20bf9d20bf110505ad40e40c1e68ed66fe8bf2ff74c988859bbab066c5ee1
BLAKE2b-256 checksum
How to use checksums
5b7eaa6a159542b80289df66f23a83833b40f9bc6a11aae01d1ad0c9fb0277b3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.10.1

Release files / dbbasic_content-0.2.0-py3-none-any.whl

Download URL dbbasic_content-0.2.0-py3-none-any.whl
Size 16.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4e59f6368c34050f9a8689c697c3337acb8b5fb87aed378afd75d9d07e36c8b5
BLAKE2b-256 checksum
How to use checksums
81551fd4de28bd603298616ba45560e64e8292de43abae8007acb692f1d8c6f1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.10.1

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page