Skip to content

dropseed/plain

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36,038 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Plain

The Python web framework for building apps.

Originally a fork of Django, reshaped over years of real use. Ready for the era of agents.

Get started

Start with an agent (Claude, Codex, Amp, OpenCode, or your agent of choice):

mkdir my-app && cd my-app && claude "$(curl -sSf https://plainframework.com/start.md)"

Or start with uv directly:

uvx plain-start my-app

Full walkthrough: https://plainframework.com/start/

What Plain code looks like

Explicit, typed, and predictable. What's good for humans is good for AI.

Models are Postgres-only:

# app/users/models.py
from plain import postgres
from plain.postgres import types
from plain.postgres.functions import Now
from plain.passwords.models import PasswordField

@postgres.register_model
class User(postgres.Model):
    email: str = types.EmailField()
    password: str = PasswordField()
    display_name: str = types.CharField(max_length=100)
    is_admin: bool = types.BooleanField(default=False)
    created_at: datetime = types.DateTimeField(default=Now())

    query: postgres.QuerySet[User] = postgres.QuerySet()

    model_options = postgres.Options(
        constraints=[
            postgres.UniqueConstraint(fields=["email"], name="unique_email"),
        ],
    )

URLs use a Router class:

# app/users/urls.py
from plain.urls import Router, path
from . import views

class UsersRouter(Router):
    namespace = "users"
    urls = [
        path("<int:pk>/", views.UserDetail),
    ]

Views are class-based:

# app/users/views.py
from plain.views import DetailView
from .models import User

class UserDetail(DetailView):
    template_name = "users/detail.html"

    def get_object(self):
        return User.query.get(pk=self.url_kwargs["pk"])

Templates are Jinja:

{# app/users/templates/users/detail.html #}
{% extends "base.html" %}

{% block content %}
<h1>{{ user.display_name }}</h1>
<p>Joined {{ user.created_at.strftime("%B %Y") }}</p>
{% endblock %}

An opinionated stack

Python where you want it, JS where you need it.

  • Python: 3.13+
  • Database: Postgres
  • Templates: Jinja2
  • Frontend: htmx, Tailwind CSS
  • Python tooling: uv (packages), ruff (lint/format), ty (type checking)
  • JavaScript tooling: oxc (lint/format), esbuild (bundling)
  • Testing: pytest

Models declare fields as annotated attributes, and that typing carries through views, forms, and URLs. plain check runs ty on every pass — what your IDE shows, CI enforces, and agents read from the same signatures.

Observability at the core

OpenTelemetry traces, a built-in request observer, and slow-query detection ship in the box. The first time an N+1 matters, you already have the tools to see it.

Agents at the forefront

Predictable APIs, typed signatures, and on-demand docs happen to be what both people and coding agents need. Plain projects also ship tooling that agents use automatically.

Rules — Always-on guardrails stored in project rules files (e.g. .claude/rules/ for Claude Code). Short files (~50 lines) that prevent the most common mistakes.

Docs — Full framework documentation, accessible on demand from the command line:

plain docs models                      # full docs
plain docs models --section querying   # one section
plain docs models --api                # typed signatures only
plain docs --search "queryset"         # search across all packages

Skills — End-to-end workflows triggered by slash commands:

  • /plain-install — add a new package and walk through setup
  • /plain-upgrade — bump versions, read changelogs, apply breaking changes, run checks
  • /plain-optimize — capture performance traces, identify slow queries and N+1 problems, apply fixes
  • /plain-bug — collect context and submit a bug report as a GitHub issue

First-party ecosystem

30 packages, one framework. All with built-in docs. Decisions that usually take a sprint are already made.

Foundation:

Backend:

Frontend:

Development:

Production:

Users:

About

Plain is a fork of Django, started in the stone age of 2023 and driven by real use at PullApprove.

About

The Python web framework for building apps.

Topics

Resources

Contributing

Security policy

Stars

Watchers

Forks

Contributors