Project Structure
Understanding how Ryx is organized will help you navigate the codebase and contribute effectively.
High-Level Layout
Ryx/
├── Cargo.toml # Rust dependencies
├── pyproject.toml # maturin build config
├── Makefile # Dev shortcuts (dev, build, test, clean)
│
├── src/ # RUST CORE (→ ryx_core.so)
│ ├── lib.rs # PyO3 module entry, QueryBuilder
│ ├── errors.rs # RyxError + PyErr conversion
│ ├── pool.rs # Global sqlx AnyPool singleton
│ ├── executor.rs # SELECT/INSERT/UPDATE/DELETE
│ ├── transaction.rs # Transaction handle + savepoints
│ └── query/
│ ├── ast.rs # QueryNode, QNode, Aggregates, Joins
│ ├── compiler.rs # AST → SQL + bound values
│ └── lookup.rs # Built-in + custom lookups
│
├── ryx/ # PYTHON PACKAGE
│ ├── __init__.py # Public API surface
│ ├── __main__.py # CLI (python -m ryx)
│ ├── models.py # Model, Metaclass, Manager
│ ├── queryset.py # QuerySet, Q, aggregates
│ ├── fields.py # 30+ field types
│ ├── validators.py # 12 validators
│ ├── signals.py # Signal system + 8 built-in signals
│ ├── transaction.py # Async transaction context manager
│ ├── relations.py # select_related / prefetch_related
│ ├── descriptors.py # FK/M2M attribute access
│ ├── exceptions.py # Exception hierarchy
│ ├── bulk.py # Bulk operations
│ ├── cache.py # Pluggable query cache
│ └── migrations/
│ ├── state.py # SchemaState + diff engine
│ ├── ddl.py # Backend-aware DDL generator
│ ├── runner.py # MigrationRunner
│ └── autodetect.py # Autodetector + file writer
│
├── tests/ # Test suites
└── examples/ # 9 progressive examples
Two Layers, One Package
Ryx is split into two layers that work together:
Rust Core (src/)
The compiled engine that handles:
- Connection pooling — Global
AnyPoolwith configurable limits - Query compilation — AST → SQL string + bound parameters
- Query execution — Async SQL via sqlx
- Type conversion — Python ↔ SQL value bridges
- Transaction management — BEGIN/COMMIT/ROLLBACK/SAVEPOINT
Python Package (ryx/)
The ergonomic API that handles:
- Model definitions — Declarative class-based models with metaclass magic
- Query building — Chainable, lazy QuerySet API
- Field types — 30+ fields with validation and type conversion
- Migrations — Schema introspection, diff detection, DDL generation
- Signals — Observer pattern for lifecycle events
- CLI — Management commands for migrations, shell, etc.
How They Connect
# Python side
posts = await Post.objects.filter(active=True).limit(10)
│
▼
# QuerySet builds a QueryNode (Python → Rust via PyO3)
│
▼
# Rust compiles QueryNode → SQL
# SELECT * FROM "posts" WHERE "active" = ? LIMIT 10
│
▼
# Rust executes via sqlx and returns rows
│
▼
# Python decodes rows into Model instances
Key Design Principles
- Immutable builders — Every QuerySet method returns a new QuerySet
- GIL minimization — Rust holds no GIL during SQL execution
- Async-native — Everything is async from the ground up
- Sync-compatible — Bridge helpers for sync environments
- Backend-agnostic — Single code path for PG, MySQL, SQLite
Next Steps
→ Models — Define your first models → Rust Core Internals — Deep dive into the compiled engine