{"id":25429779,"url":"https://github.com/rhosocial/python-activerecord","last_synced_at":"2026-05-01T04:02:50.009Z","repository":{"id":276434689,"uuid":"926338478","full_name":"rhosocial/python-activerecord","owner":"rhosocial","description":"A modern, Pythonic implementation of the ActiveRecord pattern, providing an elegant and intuitive interface for database operations with type safety and rich features.","archived":false,"fork":false,"pushed_at":"2026-05-01T00:43:00.000Z","size":4959,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-05-01T02:19:32.144Z","etag":null,"topics":["activerecord","database","orm","python"],"latest_commit_sha":null,"homepage":"https://docs.python-activerecord.dev.rho.social","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rhosocial.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-02-03T04:13:45.000Z","updated_at":"2026-04-17T14:49:53.000Z","dependencies_parsed_at":"2025-04-15T03:20:10.913Z","dependency_job_id":"32465e8f-070d-4171-9233-e4d91d878f7f","html_url":"https://github.com/rhosocial/python-activerecord","commit_stats":null,"previous_names":["rhosocial/python-activerecord"],"tags_count":23,"template":false,"template_full_name":null,"purl":"pkg:github/rhosocial/python-activerecord","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhosocial%2Fpython-activerecord","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhosocial%2Fpython-activerecord/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhosocial%2Fpython-activerecord/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhosocial%2Fpython-activerecord/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rhosocial","download_url":"https://codeload.github.com/rhosocial/python-activerecord/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhosocial%2Fpython-activerecord/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32484353,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activerecord","database","orm","python"],"created_at":"2025-02-17T02:20:28.206Z","updated_at":"2026-05-01T04:02:49.997Z","avatar_url":"https://github.com/rhosocial.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- README.md --\u003e\n# rhosocial-activerecord ($\\rho_{\\mathbf{AR}}$)\n\n[![PyPI version](https://badge.fury.io/py/rhosocial-activerecord.svg)](https://badge.fury.io/py/rhosocial-activerecord)\n[![Python](https://img.shields.io/pypi/pyversions/rhosocial-activerecord.svg)](https://pypi.org/project/rhosocial-activerecord/)\n[![Tests](https://github.com/rhosocial/python-activerecord/actions/workflows/test.yml/badge.svg)](https://github.com/rhosocial/python-activerecord/actions)\n[![Coverage Status](https://codecov.io/gh/rhosocial/python-activerecord/branch/main/graph/badge.svg)](https://app.codecov.io/gh/rhosocial/python-activerecord/tree/main)\n[![Apache 2.0 License](https://img.shields.io/github/license/rhosocial/python-activerecord.svg)](https://github.com/rhosocial/python-activerecord/blob/main/LICENSE)\n[![Powered by vistart](https://img.shields.io/badge/Powered_by-vistart-blue.svg)](https://github.com/vistart)\n\n\u003cdiv align=\"center\"\u003e\n    \u003cimg src=\"docs/images/logo.svg\" alt=\"rhosocial ActiveRecord Logo\" width=\"200\"/\u003e\n    \u003ch3\u003eA Modern, Standalone ActiveRecord Implementation for Python\u003c/h3\u003e\n    \u003cp\u003e\u003cb\u003eBuilt on Pydantic Only · Full Type Safety · True Sync-Async Parity · AI-Native Design\u003c/b\u003e\u003c/p\u003e\n\u003c/div\u003e\n\n\u003e **⚠️ Development Stage:** This project is under active development. APIs may change, and some features are not yet production-ready.\n\n## Why This Project?\n\n### 1. ActiveRecord Pattern Is Intuitive\n\nThe ActiveRecord pattern—where a class represents a table and an instance represents a row—maps directly to how developers think:\n\n```python\nuser = User(name=\"Alice\")  # Create\nuser.save()                # Persist\nuser.name = \"Bob\"          # Modify  \nuser.save()                # Update\n```\n\nSimple, consistent, and easy to reason about. **This is what Python's been missing.**\n\n### 2. Python Lacks a Standalone ActiveRecord Ecosystem\n\n| | rhosocial-activerecord | SQLAlchemy | Django ORM |\n|---|---|---|---|\n| **Pattern** | ActiveRecord | Data Mapper | ActiveRecord (coupled) |\n| **Standalone** | ✅ Yes | ✅ Yes | ❌ Django only |\n| **Dependencies** | Pydantic only | Self-contained | Django framework |\n| **Async** | Native parity | 2.0 via greenlet | Django 4.1+ only |\n\n* **SQLAlchemy** is excellent but follows the Data Mapper pattern—not ActiveRecord\n* **Django ORM** is ActiveRecord but **tightly coupled** to Django; can't use it in FastAPI, Flask, or scripts without the entire Django stack\n* **We fill the gap**: A **standalone, modern, feature-complete ActiveRecord** for all Python applications\n\n### 3. Built From Scratch, Not a Wrapper\n\n**Traditional ORM Architecture**: Your Code → ORM API → SQLAlchemy/Django → Database Driver → Database\n\n**Our Architecture**: Your Code → rhosocial-activerecord → Database Driver → Database\n\nWe built this from the ground up with **Pydantic as the only dependency**. No SQLAlchemy underneath. No Django ORM wrapper. This means zero hidden complexity, complete SQL control, a smaller footprint, and a simpler mental model — one layer to understand, not three.\n\n| | rhosocial-activerecord | SQLAlchemy | Django ORM |\n|---|---|---|---|\n| Core dependency | Pydantic only | Standalone | Django framework |\n| Query style | Expression objects + `.to_sql()` | Expression language or ORM | QuerySet chaining |\n| SQL transparency | Every query exposes `.to_sql()` | Via `compile()` | Limited via `.query` |\n| Sync/Async | Native parity (same API surface) | 2.0 async via greenlet | Async views (Django 4.1+) |\n\n## Architecture Highlights\n\n**Expression-Dialect Separation** — Query structure and SQL generation are completely decoupled. Expressions define _what_ you want; Dialects handle backend-specific SQL (SQLite, MySQL, PostgreSQL). Call `.to_sql()` on any query to inspect the generated SQL before execution.\n\n**True Sync-Async Parity** — Native implementations, not async wrappers around sync code. Same method names, same patterns, just add `await`.\n\n**Type-First Design with Pydantic v2** — Every field is type-safe, validated, and IDE-friendly. Full autocomplete support, runtime validation, and no `Any` types in public APIs.\n\n## Quick Start\n\n### Installation\n\n```bash\npip install rhosocial-activerecord\n```\n\n### End-to-End Example\n\n```python\n\"\"\"Save as demo.py and run with: python demo.py\"\"\"\nfrom rhosocial.activerecord.model import ActiveRecord\nfrom rhosocial.activerecord.backend.impl.sqlite import SQLiteBackend\nfrom rhosocial.activerecord.backend.impl.sqlite.config import SQLiteConnectionConfig\nfrom rhosocial.activerecord.backend.expression import ColumnDefinition, CreateTableExpression\nfrom rhosocial.activerecord.backend.expression.statements import (\n    ColumnConstraint, ColumnConstraintType\n)\nfrom rhosocial.activerecord.base import FieldProxy\nfrom typing import ClassVar, Optional\nfrom pydantic import Field\n\n\nclass User(ActiveRecord):\n    __table_name__ = \"users\"\n    id: Optional[int] = None # Primary key\n    name: str = Field(max_length=100)\n    email: str\n    age: int = 0\n    c: ClassVar[FieldProxy] = FieldProxy()\n\n\n# Configure backend (in-memory SQLite for demo)\nconfig = SQLiteConnectionConfig(database=\":memory:\")\nUser.configure(config, SQLiteBackend)\n\n# Create table using DDL expression (type-safe, no raw SQL)\ncreate_table = CreateTableExpression(\n    dialect=User.__backend__.dialect,\n    table_name=\"users\",\n    columns=[\n        ColumnDefinition(\"id\", \"INTEGER\",\n            constraints=[ColumnConstraint(ColumnConstraintType.PRIMARY_KEY)]),\n        ColumnDefinition(\"name\", \"VARCHAR(100)\",\n            constraints=[ColumnConstraint(ColumnConstraintType.NOT_NULL)]),\n        ColumnDefinition(\"email\", \"VARCHAR(255)\"),\n        ColumnDefinition(\"age\", \"INTEGER\"),\n    ]\n)\nUser.__backend__.execute(create_table)\n\n# Insert\nalice = User(name=\"Alice\", email=\"alice@example.com\", age=30)\nalice.save()\n\n# Query with type-safe expressions\nadults = User.query().where(User.c.age \u003e= 18).all()\n\n# Inspect generated SQL without executing\nsql, params = User.query().where(User.c.age \u003e= 18).to_sql()\n# SQL: SELECT * FROM \"users\" WHERE \"users\".\"age\" \u003e= ?\n# Params: (18,)\n```\n\n### Relationships\n\n```python\nfrom rhosocial.activerecord.model import ActiveRecord\nfrom rhosocial.activerecord.backend.impl.sqlite import SQLiteBackend\nfrom rhosocial.activerecord.backend.impl.sqlite.config import SQLiteConnectionConfig\nfrom rhosocial.activerecord.backend.expression import ColumnDefinition, CreateTableExpression\nfrom rhosocial.activerecord.backend.expression.statements import (\n    ColumnConstraint, ColumnConstraintType,\n    TableConstraint, TableConstraintType,\n    ForeignKeyConstraint, ReferentialAction\n)\nfrom rhosocial.activerecord.base import FieldProxy\nfrom rhosocial.activerecord.relation import HasMany, BelongsTo\nfrom typing import ClassVar, Optional\n\n\nclass Author(ActiveRecord):\n    __table_name__ = \"authors\"\n    id: Optional[int] = None\n    name: str\n    c: ClassVar[FieldProxy] = FieldProxy()\n    posts: ClassVar[HasMany[\"Post\"]] = HasMany(foreign_key=\"author_id\")\n\n\nclass Post(ActiveRecord):\n    __table_name__ = \"posts\"\n    id: Optional[int] = None\n    title: str\n    author_id: int\n    c: ClassVar[FieldProxy] = FieldProxy()\n    author: ClassVar[BelongsTo[\"Author\"]] = BelongsTo(foreign_key=\"author_id\")\n\n\n# Configure backend\nconfig = SQLiteConnectionConfig(database=\":memory:\")\nAuthor.configure(config, SQLiteBackend)\nPost.__backend__ = Author.__backend__\n\n# Create tables using DDL expressions\ndialect = Author.__backend__.dialect\n\nAuthor.__backend__.execute(CreateTableExpression(\n    dialect=dialect,\n    table_name=\"authors\",\n    columns=[\n        ColumnDefinition(\"id\", \"INTEGER\",\n            constraints=[ColumnConstraint(ColumnConstraintType.PRIMARY_KEY)]),\n        ColumnDefinition(\"name\", \"TEXT\"),\n    ]\n))\n\nAuthor.__backend__.execute(CreateTableExpression(\n    dialect=dialect,\n    table_name=\"posts\",\n    columns=[\n        ColumnDefinition(\"id\", \"INTEGER\",\n            constraints=[ColumnConstraint(ColumnConstraintType.PRIMARY_KEY)]),\n        ColumnDefinition(\"title\", \"TEXT\"),\n        ColumnDefinition(\"author_id\", \"INTEGER\"),\n    ],\n    constraints=[\n        ForeignKeyConstraint(\n            columns=[\"author_id\"],\n            reference_table=\"authors\",\n            reference_columns=[\"id\"],\n            on_delete=ReferentialAction.CASCADE\n        )\n    ]\n))\n\n# Eager loading — one query, no N+1\nauthors = Author.query().with_(\"posts\").all()\nfor author in authors:\n    print(f\"{author.name}: {[p.title for p in author.posts()]}\")\n```\n\n\u003e **Async Parity:** All sync APIs have async counterparts. For async models, use `AsyncActiveRecord`, `AsyncHasMany`, and `AsyncBelongsTo` instead. The API surface is identical—just add `await`.\n\u003e\n\u003e ⚠️ **Note:** The built-in SQLite async backend is currently for testing only. For other backends (MySQL, PostgreSQL, etc.), async support depends on the specific implementation.\n\n## Features\n\nAll features support both **sync** and **async** APIs with identical method names—just add `await`.\n\n### DDL Expressions\n\nType-safe schema definition without raw SQL:\n\n* **[CreateTableExpression](src/rhosocial/activerecord/backend/expression/statements/ddl_table.py)** — Create tables with columns and constraints\n* **[DropTableExpression](src/rhosocial/activerecord/backend/expression/statements/ddl_table.py)** — Drop tables with IF EXISTS support\n* **[AlterTableExpression](src/rhosocial/activerecord/backend/expression/statements/ddl_alter.py)** — Add/drop columns, rename tables\n* **[CreateIndexExpression](src/rhosocial/activerecord/backend/expression/statements/ddl_index.py)** — Create indexes with IF NOT EXISTS\n* **[CreateViewExpression](src/rhosocial/activerecord/backend/expression/statements/ddl_view.py)** — Create views from query expressions\n\n### Query Builders\n\nThree core query types, each with full sync/async parity:\n\n* **[ActiveQuery](src/rhosocial/activerecord/query/active_query.py)** / **[AsyncActiveQuery](src/rhosocial/activerecord/query/active_query.py)** — Model-based queries with WHERE, JOIN, ORDER BY, LIMIT, aggregations, and [eager loading](src/rhosocial/activerecord/query/relational.py) via `.with_()`\n* **[CTEQuery](src/rhosocial/activerecord/query/cte_query.py)** / **[AsyncCTEQuery](src/rhosocial/activerecord/query/cte_query.py)** — Common Table Expressions (WITH clauses) for recursive queries and complex multi-step operations\n* **[SetOperationQuery](src/rhosocial/activerecord/query/set_operation.py)** / **[AsyncSetOperationQuery](src/rhosocial/activerecord/query/set_operation.py)** — UNION, INTERSECT, EXCEPT operations\n\n### Relationships\n\nType-safe relationship descriptors with eager loading support:\n\n* **[BelongsTo](src/rhosocial/activerecord/relation/__init__.py)** / **[AsyncBelongsTo](src/rhosocial/activerecord/relation/__init__.py)** — Child-to-parent associations\n* **[HasOne](src/rhosocial/activerecord/relation/__init__.py)** / **[AsyncHasOne](src/rhosocial/activerecord/relation/__init__.py)** — One-to-one parent-to-child\n* **[HasMany](src/rhosocial/activerecord/relation/__init__.py)** / **[AsyncHasMany](src/rhosocial/activerecord/relation/__init__.py)** — One-to-many parent-to-children\n\n### Field Mixins\n\nReusable mixins for common model behaviors:\n\n* **[OptimisticLockMixin](src/rhosocial/activerecord/field/version.py)** — Version-based concurrency control\n* **[SoftDeleteMixin](src/rhosocial/activerecord/field/soft_delete.py)** — Logical deletion with `deleted_at` timestamp\n* **[TimestampMixin](src/rhosocial/activerecord/field/timestamp.py)** — Auto-managed `created_at` and `updated_at`\n* **[UUIDMixin](src/rhosocial/activerecord/field/uuid.py)** — UUID primary keys\n\n### Model Events\n\nLifecycle hooks for custom business logic:\n\n* **[Model Events](src/rhosocial/activerecord/interface/base.py)** — `before_insert`, `after_insert`, `before_update`, `after_update`, `before_delete`, `after_delete`\n\nFor details, see the [documentation](docs/en_US/).\n\n## Backend Support\n\n| Backend | Package | Link | Sync | Async |\n|---|---|---|---|---|\n| **SQLite** | Built-in | — | ✅ Stable | ✅ Stable |\n| **MySQL** | `rhosocial-activerecord-mysql` | [PyPI](https://pypi.org/project/rhosocial-activerecord-mysql/) \\| [GitHub](https://github.com/rhosocial/python-activerecord-mysql) | 🔄 In progress | 🔄 In progress |\n| **MariaDB** | `rhosocial-activerecord-mariadb` | [PyPI](https://pypi.org/project/rhosocial-activerecord-mariadb/) \\| [GitHub](https://github.com/rhosocial/python-activerecord-mariadb) | 🔄 In progress | 🔄 In progress |\n| **PostgreSQL** | `rhosocial-activerecord-postgres` | [PyPI](https://pypi.org/project/rhosocial-activerecord-postgres/) \\| [GitHub](https://github.com/rhosocial/python-activerecord-postgres) | 🔄 In progress | 🔄 In progress |\n| **Oracle** | `rhosocial-activerecord-oracle` | [PyPI](https://pypi.org/project/rhosocial-activerecord-oracle/) \\| [GitHub](https://github.com/rhosocial/python-activerecord-oracle) | 🔄 In progress | 🔄 In progress |\n| **SQL Server** | `rhosocial-activerecord-sqlserver` | [PyPI](https://pypi.org/project/rhosocial-activerecord-sqlserver/) \\| [GitHub](https://github.com/rhosocial/python-activerecord-sqlserver) | 🔄 In progress | 🔄 In progress |\n\n## Requirements\n\n* **Python**: 3.8+ (including 3.13t/3.14t free-threaded builds)\n* **Core Dependency**: Pydantic 2.10+ (Python 3.8) or 2.12+ (Python 3.9+)\n* **SQLite**: 3.25+ (for the built-in backend)\n\nSee [Python Version Support](docs/en_US/introduction/python_version_support.md) for detailed compatibility.\n\n## Get Started with AI Code Agents\n\nThis project ships with built-in configurations for AI code agents and editors. Clone the repo and launch your preferred tool — project-specific skills, commands, and context files are discovered automatically.\n\n```bash\ngit clone https://github.com/rhosocial/python-activerecord.git\ncd python-activerecord\n```\n\n### CLI Code Agents\n\n| Tool | How to start | What's included |\n|---|---|---|\n| [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `claude` | `CLAUDE.md` project instructions + `.claude/skills/` (5 skills) + `.claude/commands/` |\n| [OpenCode](https://github.com/opencode-ai/opencode) | `opencode` | `.opencode/commands/` (8 slash commands) + `.opencode/hints.yml` |\n| [Codex](https://github.com/openai/codex) | `codex` | `AGENTS.md` project context |\n\n### Editors\n\n[Cursor](https://cursor.com) and [Windsurf](https://windsurf.com) users can open the project folder directly. Both editors benefit from the `CLAUDE.md` and `AGENTS.md` context files at the project root, as well as the `docs/LLM_CONTEXT.md` structured reference.\n\n### For Any LLM\n\nFeed [`docs/LLM_CONTEXT.md`](docs/LLM_CONTEXT.md) to your preferred LLM for a structured overview of the project's architecture, module map, and key concepts.\n\n### What can AI agents do?\n\nSee the **[AI-Assisted Development Guide](docs/en_US/introduction/ai_assistance.md)** for concrete examples of what AI agents can help you accomplish with this project — from generating models and queries to implementing new backends and running tests.\n\n## Documentation\n\n* **[Getting Started Guide](docs/en_US/getting_started/)** — Installation and basic usage\n* **[AI-Assisted Development](docs/en_US/introduction/ai_assistance.md)** — Using AI code agents with this project\n* **[Modeling Guide](docs/en_US/modeling/)** — Defining models, fields, and relationships\n* **[Querying Guide](docs/en_US/querying/)** — Complete query builder documentation\n* **[Backend Development](docs/en_US/backend/)** — Creating custom database backends\n* **[Architecture Overview](docs/ARCHITECTURE.md)** — Module structure and design decisions\n* **[LLM Context](docs/LLM_CONTEXT.md)** — Structured context for AI assistants\n* **[API Reference](https://docs.python-activerecord.dev.rho.social/api/)** — Full API documentation\n\n## Contributing\n\nWe welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n## License\n\n[Apache License 2.0](LICENSE) — Copyright © 2026 [vistart](https://github.com/vistart)\n\n---\n\n\u003cdiv align=\"center\"\u003e\n    \u003cp\u003e\u003cb\u003eBuilt with ❤️ by the rhosocial team\u003c/b\u003e\u003c/p\u003e\n    \u003cp\u003e\u003ca href=\"https://github.com/rhosocial/python-activerecord\"\u003eGitHub\u003c/a\u003e · \u003ca href=\"https://docs.python-activerecord.dev.rho.social/\"\u003eDocumentation\u003c/a\u003e · \u003ca href=\"https://pypi.org/project/rhosocial-activerecord/\"\u003ePyPI\u003c/a\u003e\u003c/p\u003e\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frhosocial%2Fpython-activerecord","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frhosocial%2Fpython-activerecord","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frhosocial%2Fpython-activerecord/lists"}