PyForge
Rust-accelerated serialization and validation for Django — drop-in, zero rewrite.
What Is PyForge
PyForge moves Django REST Framework's serialization and validation hot paths from Python to Rust. It reads your existing Django model definitions, compiles a typed schema once at startup, and processes field values through Rust-native code on every request. The result is 3-8x faster API responses for list views, bulk operations, and any endpoint that touches more than a handful of records.
It is designed for Django developers running production APIs on DRF who need better throughput without migrating to a different framework.
Benchmarks
Measured on CPython 3.12, Rust 1.93, macOS ARM64.
Model: 9-field RentalApplication (CharField, DecimalField, DateTimeField, UUIDField, etc.)
| Scenario | Pure Python (est.) | PyForge | Speedup |
|---|---|---|---|
| Serialize 100 records | 1.5-2.5 ms | 340 µs | 3-5x |
| Serialize 1,000 records | 15-25 ms | 3.4 ms | 4-6x |
| Serialize 10,000 records | 150-250 ms | 40.4 ms | 4-6x |
| Validate 1,000 fields | 2-4 ms | 321 µs | 5-8x |
| Validate 10,000 fields (parallel) | 20-40 ms | ~3 ms | 6-10x |
Small batches (<10 records) show minimal gain due to the Python/Rust bridge overhead (~5-8µs per call).
Full methodology and per-field-type breakdown: BENCHMARKS.md
Installation
Add the app to your Django settings:
=
Quickstart
# 1. Compile a schema once (e.g., in AppConfig.ready)
=
# 2. Serialize a single instance — one Rust call, all fields
=
# 3. Validate an instance against the schema
=
# 4. Or use the DRF mixin — zero changes to your existing code
=
=
Supported Django Field Types
| Django Field | Rust Type | Notes |
|---|---|---|
| CharField | String |
max_length enforced in characters, not bytes |
| TextField | String |
No length limit |
| IntegerField | i32 |
Full range |
| BigIntegerField | i64 |
Full range |
| DecimalField | rust_decimal::Decimal |
Full precision preserved — never converted to float |
| DateField | chrono::NaiveDate |
ISO 8601 format |
| DateTimeField | chrono::DateTime<Utc> |
RFC 3339 with timezone (USE_TZ=True) |
| TimeField | chrono::NaiveTime |
ISO 8601 format |
| UUIDField | uuid::Uuid |
Hyphenated and non-hyphenated accepted |
| BooleanField | bool |
Serialized as True/False, never as 1/0 |
| FloatField | f64 |
NaN and Infinity rejected at serialization |
| JSONField | serde_json::Value |
Full nested structure preserved |
| BinaryField | Vec<u8> |
Base64 encoded for JSON transport |
| EmailField | String |
max_length enforced |
| URLField | String |
max_length enforced |
| SlugField | String |
Character set validated |
Requirements
- Python 3.11 or newer
- Django 4.2 LTS or 5.x
- Rust 1.75+ (only needed when building from source)
- Platforms: Linux, macOS, Windows (manylinux wheels published to PyPI)
Architecture
PyForge has three layers. ModelSchema is a Rust #[pyclass] that reads
Django's _meta API once and compiles field descriptors into a cached struct.
serialize_instance takes a Django model object and the cached schema, extracts
every field value via getattr in a single Rust call, converts each to a native
Rust type (Decimal, DateTime, UUID, etc.), serializes to JSON-compatible output,
and returns a Python dict. RustSerializerMixin sits on top as a DRF mixin that
automatically classifies which fields are simple model fields (Rust-accelerated)
and which are computed fields (delegated back to DRF). For validation batches
above 64 fields, Rayon distributes work across CPU cores while the GIL is released.
Crates
| Crate | Description |
|---|---|
pyforge |
Core Rust-Python binding library |
pyforge-django |
Django integration layer |
pyforge-ffi |
CPython C API bindings |
pyforge-macros |
Procedural macros |
pyforge-build-config |
Build-time Python detection |
License
MIT — Abdulwahed Mansour