Expand description
rustango — a Django-shaped, batteries-included web framework for Rust.
ORM with auto-migrations, auto-admin, multi-tenancy, sessions +
JWT + OAuth2/OIDC + HMAC auth, DRF-style serializers + viewsets,
signals, caching, media (S3/R2/B2/MinIO), email pipeline,
background jobs, scheduled tasks, OpenAPI 3.1 auto-derive, every
standard middleware. Postgres + MySQL + SQLite through the
same &Pool API, opt-in via Cargo features.
[dependencies]
rustango = "0.41" # Postgres (default)
rustango = { version = "0.41", features = ["sqlite"] } # SQLite
rustango = { version = "0.41", features = ["mysql"] } # MySQL 8.0+§What’s new in v0.41 (May 2026) — Tier 1 ORM gap-closure batch
Django-shape syntax with Rust-shape compile-time safety. Ten ORM tickets across 14 PRs (epic #273):
Q!()compile-time macro (#269) —Q!(User.email__icontains = "alice")expands at parse time to the typed-column call. Typo’d field names fail the build.query::Qruntime composable predicate (#263) — for dynamic filter trees (admin chips, REST query params). Operator overloads:&/|/^/!.QuerySet::distinct_on(#264) — PGDISTINCT ONnative +ROW_NUMBER()portable fallback on MySQL/SQLite. The “latest per group” pattern, tri-dialect.Model::bulk_upsert_pool/Model::bulk_insert_or_ignore_pool(#267) — Djangobulk_create(update_conflicts=True)/(ignore_conflicts=True)across PG, MySQL, and SQLite.#[rustango(unique_when(...))](#265) — partial unique indexes (DjangoUniqueConstraint(condition=Q(...))). PG/SQLite native; MySQL warns + falls back.- DB functions batch 1 (#266) — Cast, LPad, RPad, MD5, SHA1,
SHA256, Position, Repeat, Reverse, Sign, Mod, Power, Sqrt — per-
dialect emission with clean
NotSupportederrors on SQLite for the genuinely-missing items (hashes, Reverse, Power/Sqrt withoutSQLITE_ENABLE_MATH_FUNCTIONS). AggregateBuilder::alias()(#268) — Django 3.2 non- projected annotation. Filter/order by a derived aggregate without paying the column-decode cost.#[rustango(manager(ext = "..."))](#271) — derive emits the custom-manager extension trait next to the model.sql::explain_pool(#272) — tri-dialect query plan helper. PGEXPLAIN (FORMAT JSON, ANALYZE, BUFFERS), MySQLEXPLAIN ANALYZE/FORMAT=TREE/FORMAT=JSON, SQLiteEXPLAIN QUERY PLAN.- PG-typed legacy executor deleted (#270, 4 waves) — the
bi-API confusion is gone.
Fetcher/Counter/Updater/Deleterextension traits + the&PgPoolstandalone fns +transaction/count_rows/raw_execute/bulk_updateare removed. The_onfamily moved to a#[doc(hidden)]module for macro use only. Migration: drop the trait imports, switch.fetch(pool)→.fetch_on(pool)(inherent on QuerySet),.delete(pool)→.delete_on(pool),.execute(pool)(on UpdateBuilder) →.execute_on(pool). Use the_poolfamily for tri-dialect calls.
Full ticket index: PRs #274–#286.
§What’s new in v0.40 (May 2026)
- Admin session auth without tenancy (epic #253) — bare
adminnow ships a styled/loginform, signed-cookie sessions (session::SessionSecret), sidebar Logout, password change at/account/password,manage create-adminCLI verb, andis_superusergating. Opt in viaadmin::Builder::with_session_auth. Same signing key works across [tenancy::session] +admin::session(different cookie names + payloads so cookies never cross-decode). - GenericForeignKey ergonomics + admin inlines (epic #246)
—
#[rustango(generic_fk(name, ct_column, pk_column))]emits typedtarget_pool()accessor +set_target_for::<T>()setter. Admin list view collapses the(ct_id, object_pk)pair into one clickable target link. Newregister_admin_inline_generic!renders polymorphic children as inline panels on the parent’s detail + edit pages. ContentType<select>picker replaces raw integer inputs on the standalone create/edit form. - Field
help_text—#[rustango(help_text = "…")]on any field renders a muted caption below the admin form input. The string lives oncore::FieldSchema::help_textso future surfaces (DRF schemas, OpenAPI descriptions) can read the same source. - Shared
.btnfamily — admin + operator-console buttons, links, sidebar Logout, inline-panel row links all look like one product. Consistent across bare admin / tenant admin / operator console. - Reusable foundations —
session::SessionSecret+session::sign(HMAC primitive) andmanage_interactive(TTY prompts) promoted to the crate root so the bare admin can reuse them withouttenancycompiled in. - Runnable demo:
examples/gfk_demoexercises every new admin + GFK surface end-to-end on SQLite.
Defaults pull postgres, admin, and runserver (the bi-dialect
single-pool server::AppBuilder). Add "tenancy" for the
multi-tenant resolver / pools / per-tenant auth. Tenancy is
tri-dialect since v0.33: [tenancy::TenantPools] is generic over
the backend, database-mode tenants work on every backend, and
schema-mode stays Postgres-only by language semantics. Add
"mysql" or "sqlite" for additional backends — the same
#[derive(Model)] types and _pool ORM API work against any of
them. Drop default-features for the bare ORM (no axum, no Tera).
§Quick start
cargo install cargo-rustango
cargo rustango new myblog # default: ORM + admin
cargo rustango new myapi --template api # JSON-only, no admin
cargo rustango new shop --template tenant # multi-tenancy + operator consoleThen:
cd myblog
cp .env.example .env # set DATABASE_URL
cargo run -- migrate # apply bootstrap migrations
cargo run # http://localhost:8080§Unified manage runner (since v0.16)
Since v0.16 there is one binary that serves HTTP and dispatches
manage commands. cargo run starts the server; cargo run -- <verb>
runs a CLI command. A scaffolded src/main.rs looks like:
#[rustango::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let _ = dotenvy::dotenv();
rustango::manage::Cli::new()
.api(myblog::urls::router())
.tenancy() // optional, with `tenancy` feature
.migrations_dir(std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("migrations"))
.run()
.await
}Common manage verbs (every command supports --help):
| Group | Verbs |
|---|---|
| Migrations | makemigrations, migrate [target], downgrade [N], showmigrations, add-data-op |
| Scaffolders | startapp <name>, make:viewset, make:serializer, make:form, make:job, make:notification, make:middleware, make:test |
| System | about, check, check --deploy, version, docs |
| Tenancy | create-tenant, create-operator, create-user, list-tenants, create-api-key, grant-perm, revoke-perm, audit-cleanup |
§Full reference
See the workspace README
for the complete feature matrix, ORM cookbook, viewset / serializer
recipes, multi-tenancy guide, and production checklist. The
examples/cookbook_blog
crate ships a runnable multi-tenant blog with one chapter per
feature surface.
Re-exports§
pub use sql::Auto;pub use rustango_macros as macros;
Modules§
- access_
log - HTTP access log middleware — one tracing event per request with
method / path / status / duration / IP. See
access_log::AccessLogLayer. HTTP access log middleware — emit one tracing event per request. - account_
lockout - Per-account login lockout — defends against credential stuffing.
Cache-backed counter + lock flag. See
account_lockout::Lockout. Per-account login lockout — defends against credential stuffing / brute-force attacks that bypass per-IP rate limits. - admin
- Auto-generated CRUD admin for rustango models.
- api_
errors - Standardized API error responses. See
api_errors::ApiError. Standardized API error responses. - api_
keys - Generic API key generation + verification (argon2id-hashed).
See
api_keys::generate_key/api_keys::verify_key. Generic API key generation and verification. - api_
version - API versioning — extract version from header / query / URL prefix.
See
api_version::VersionStrategyandapi_version::ApiVersion. API versioning extractor — read the requested API version from URL, header, or query parameter. - audit
- Audit log — single composite-key table that captures every
tracked write (insert, update, delete, soft-delete) across every
model whose declaration carries
#[rustango(audit(...))]. - auth_
backends - Pluggable
AUTHENTICATION_BACKENDSchain — register an ordered list ofauth_backends::AuthBackendimpls; the chain returns the first non-Noneresult. ShipsRemoteUserBackendfor SSO proxy deployments. Issue #54 (partial). Pluggable authentication-backend chain — Django’sAUTHENTICATION_BACKENDS = [...]setting. - auth_
decorators - Django-shape access decorators —
login_requiredmiddleware +?next=round-trip helpers. Issue #11. Django-shape access decorators —login_requiredmiddleware +?next=round-trip helpers. Issue #11. - auth_
flows - Pre-built auth flows — password reset, email verification, magic-link login.
See
auth_flows::PasswordReset/auth_flows::EmailVerification. Pre-built auth flows — password reset + email verification. - base36
- Django-shape base36 integer encoding — used in password reset
URLs and other URL-friendly opaque IDs.
int_to_base36(n)+base36_to_int(s)round-trip. Django-shape base36 integer encoding —int_to_base36/base36_to_int. - base62
- Django-shape base62 integer encoding —
[0-9A-Za-z]62-char alphabet for short URL IDs (YouTube / bit.ly style). Same shape as base36 but case-sensitive, giving 6.0 bits per char. Django-shape base62 integer encoding —int_to_base62/base62_to_int. - body_
limit - Request body size limit middleware — fast
Content-Lengthrejection returning structured413 Payload Too LargeJSON. Complements axum’s per-extractorDefaultBodyLimit. Seebody_limit::BodyLimitLayer. Request body size limit middleware. - cache
- Pluggable caching layer —
cache::Cachetrait +cache::NullCache+cache::InMemoryCache. Redis backend behind thecache-redisfeature. Pluggable caching layer. - cache_
fragment - Fragment caching — Django’s
{% cache %}template tag.cache_fragment::cached_renderis a handler-side wrapper that consults the cache before invoking a compute closure. Tera lacks the block-tag extension API a 1:1 port would need; the handler-side helper composes naturally with axum and passes the cached HTML through the template context. Issue #16. Fragment caching — Django’s{% cache %}template tag. - casts
- Attribute casts (#819) —
Cast<C>field wrapper +CastValuetrait, with theEncryptedStringAEAD-at-rest built-in. Attribute casts — a per-field get/set transform layer (Eloquent$casts/CastsAttributes; Djangofrom_db_value/get_prep_value). Issue #819. - composite_
pk - Composite-primary-key patterns — Django 5.2’s
CompositePrimaryKeymapped toAuto<i64>surrogate +unique_together. Seecomposite_pkfor the canonical worked pattern. Issue #46. Composite-primary-key patterns — Django 5.2’sCompositePrimaryKeyadapted to rustango idioms. Issue #46. - compression
- Response compression middleware — gzip + deflate via flate2.
Honors
Accept-Encoding, skips already-compressed bodies and binary content-types. Seecompression::CompressionLayer. Response compression middleware — gzip + deflate. - config
- Layered TOML configuration (slice 8.3).
- content_
negotiation - HTTP content negotiation — pick the best response format from the
client’s
Acceptheader. Seecontent_negotiation::negotiate. HTTP content negotiation — pick the best response format from the client’sAcceptheader. - contenttypes
- ContentType framework (Django-shape) — runtime handle for “any registered model” used by permissions, generic foreign keys, soft-FK prefetch, audit-history admin panels, etc. Sub-slice F.1 of the v0.15.0 plan. Django-shape ContentType framework — sub-slice F.1 of v0.15.0.
- cookies
- Django-shape
Set-Cookiebuilder —Cookie::new(name, value) .path("/").max_age(secs).http_only().secure().same_site(...) .build()produces an axum-readyHeaderValue. Replaces scattered hand-rolled cookie strings acrossmessages/csrf/jwt/admin/tenancymodules. Django-shapeSet-Cookiebuilder. - core
- Core types for rustango.
- cors
- CORS middleware —
cors::CorsLayerfor axum routers. CORS middleware — Cross-Origin Resource Sharing for axum routers. - csp_
nonce - Per-request CSP nonce middleware — generates a fresh CSPRNG nonce
per request, makes it available via
Extension<Nonce>, and substitutes'nonce-__RUSTANGO_NONCE__'in the CSP header so inline<script nonce="...">tags pass strict CSP. Seecsp_nonce::CspNonceLayer. Per-request CSP nonce middleware. - csv
- Minimal RFC 4180 CSV writer — zero deps. See
csv::CsvWriter. Minimal CSV writer — RFC 4180 compliant, zero deps. - csv_
response - axum response wrapper for CSV exports — sets Content-Type +
optional Content-Disposition so browsers prompt a “Save as…”
download. See
csv_response::CsvResponse. axum response wrapper for CSV exports — sets the rightContent-Type+ an optionalContent-Dispositionso browsers prompt a “Save as…” download. - databases
- Named multi-database registry +
QuerySet::using(alias)(#332/#400). Named multi-database registry — Django’sDATABASESsetting +QuerySet.using(alias)(issues #332 / #400). - dateformat
- Django-shape date format-character expander — converts a
Django template
{{ obj|date:"Y-m-d H:i" }}format string into the matchingstrftimeoutput. Mirrorsdjango.utils.dateformat. Django-shape date format-character expander. - dateparse
- Django-shape ISO 8601 date / time / datetime / duration string
parsers —
dateparse::{parse_date, parse_time, parse_datetime, parse_duration}. Mirrorsdjango.utils.dateparse.*. Django-shape ISO 8601 / W3C datetime parsers. - dates
- Django
django.utils.datesparity — month / weekday name lookups:month_full(m),month_abbr(m),month_ap(m),weekday_full(d),weekday_abbr(d). English-only by design (i18n is a separate concern). Djangodjango.utils.datesparity — month / weekday name maps. - dbshell
manage dbshell— spawnpsql/mysql/sqlite3for the currentDATABASE_URL. Issue #56 (partial).manage dbshell— spawn the native CLI client (psql,mysql,sqlite3) for the currentDATABASE_URL. Django’sdbshell. Issue #56 (partial).- default_
filters - Django
defaultfilterstemplate filters —pluralize,truncatewords,linebreaks,default_if_none. Seedefault_filters. Issue #61. Djangodefaultfilterstemplate filters as Tera filters. Issue #61. - distributed_
lock - Distributed locks backed by
cache::Cache— “only one worker at a time runs this task” with TTL-based crash recovery and token-checked release. Seedistributed_lock::DistributedLock. Distributed locks backed byCache. - Email backends —
email::Mailertrait + console/in-memory/null backends. Email backend layer — pluggable async email sending. - email_
jobs - Send email off the request path via the
crate::jobsqueue. Pairs theemail::Mailertrait with the queue’s retry-with- backoff so transient SMTP failures don’t blow up handlers. Seeemail_jobs::register_email_job+email_jobs::dispatch_email. Send email off the request path via thecrate::jobsqueue. - email_
templates - Tera-rendered email helpers — bridge
crate::emailmailers and the Tera templating engine. Each email =<name>.subject.txt+<name>.txt+ optional<name>.html. Seeemail_templates::EmailRenderer. Tera-rendered email helpers — bridge the existingcrate::emailmailer trait and the Tera templating engine (already a dep on theadminfeature). - env
- Typed environment variable readers —
required/with_default/optional/list/duration_secs/duration_millis. Typed environment variable readers — pydantic-settings / django-environ shape. - etag
- ETag middleware — hashes 2xx response bodies, returns 304 when
If-None-Matchmatches. Seeetag::EtagLayer. ETag middleware — hashes response bodies and serves304 Not Modifiedwhen the client’sIf-None-Matchmatches. - events
- Domain event bus — typed pub-sub for application-level events
decoupled from the ORM. Use this for cross-component fanout
(
OrderPlaced→ mail + billing + audit handlers); usesignalsfor per-Model lifecycle hooks. Seeevents::EventBus. Domain event bus — typed pub-sub for application-level events decoupled from the ORM. Closes future-backlog item #45 (“internal event bus / domain event dispatch decoupled from ORM signals”). - feature_
flags - Feature flags / killswitches backed by
cache::Cache— boolean killswitch + per-user override + stable percentage rollout. Seefeature_flags::FeatureFlags. Feature flags / killswitches backed by theCachetrait. - fixtures
- Test fixture loader — seed a database from JSON files.
See
fixtures::Fixture. Test fixture loader — seed a test database from JSON files. - flatpages
- Static “flat pages” —
django.contrib.flatpages. Build aflatpages::FlatPageMap(path →flatpages::FlatPage) and mountflatpages::flatpages_middlewareon your axum router; matching requests serve the page body withtext/html(or a custom content-type). Bring your own Tera wrapping. Issue #57. Static “flat pages” —django.contrib.flatpages. - forms
- Form parsing, validation, and saving — shared between the auto-admin and user route handlers.
- health
- Health check endpoints —
/health(liveness) +/ready(readiness). Seehealth::health_router. Health check endpoints —/health(liveness) and/ready(readiness). - hmac_
auth - AWS-style canonical request signed with SHA-256, replay-bounded
by an X-Date tolerance window. See
hmac_auth::HmacAuthLayer. HMAC-signed request authentication for service-to-service traffic. - host_
validation - Host-header allowlist middleware — Django
ALLOWED_HOSTSparity. Seehost_validation::AllowedHostsLayer. Host-header allowlist middleware — Django parity for theALLOWED_HOSTSsetting + the host-validation stepSecurityMiddlewareruns implicitly before every view. - http_
client - Opinionated HTTP client —
reqwestwrapper with sane timeouts, retry on idempotent verbs / transient failures, default User-Agent. Seehttp_client::HttpClient. Opinionated HTTP client —reqwestwrapper with sane timeouts, exponential-backoff retries on idempotent verbs / transient failures, and a defaultUser-Agent. - http_
date - Django-shape HTTP date parser + formatter — RFC 1123 / RFC 850 /
ANSI C asctime parsing per RFC 7231 §7.1.1.1; IMF-fixdate output
per RFC 1123.
http_date::http_date(secs)+parse_http_date(s). Django-shape HTTP date parser + formatter. - http_
methods - Django-shape
@require_http_methods(['GET', 'POST'])/@require_GET/@require_POST/@require_safedecorators as tower middleware. Rejects unallowed methods with405 Method Not Allowed+ RFC 7231-compliantAllow:header listing the accepted set. Django-shape HTTP method restriction layer — mirrorsdjango.views.decorators.http.{require_http_methods, require_GET, require_POST, require_safe}. - humanize
- Django
humanizetemplate filters —intcomma,intword,naturalsize,ordinal,apnumber,naturaltime,naturalday. Seehumanize. Issue #17. Djangohumanizetemplate filters as Tera filters. Issue #17. - i18n
- Internationalization (i18n) — translation lookups + Accept-Language negotiation.
See
i18n::Translatorandi18n::negotiate_language. Internationalization (i18n) — translation lookups + Accept-Language negotiation. - idempotency
- Idempotency-key middleware (Stripe-shape) — replays a stored
successful response when a write request arrives with the same
Idempotency-Key. Backed by anycache::Cache. Seeidempotency::IdempotencyLayer. Idempotency-key middleware (Stripe-shape). - inheritance
- Model-inheritance patterns — Django’s
Meta.abstract, multi-table, and proxy shapes adapted to Rust idioms. Seeinheritancefor the canonical worked mappings. Issue #51. Model-inheritance patterns — Django’sMeta.abstract, multi-table, and proxy shapes adapted to Rust. Issue #51. - ip_
filter - IP allowlist / blocklist middleware. See
ip_filter::IpFilterLayer. IP allowlist / blocklist middleware — gate routes by client IP. - jobs
- Background job queue with worker pool — async work outside the request
lifecycle. Currently in-memory only. See
jobs::JobQueue. Background job queue — async work outside the request lifecycle. - jsonapi
- JSON:API v1.1 response envelope adapter — wrap a flat
serde_json::Value(typicalserializeroutput) into the{"data": {"type", "id", "attributes": {...}}}shape that JSON:API clients expect. Seejsonapi::to_resource+jsonapi::to_collection. JSON:API v1.1 response shape adapter. - jti_
store - Pluggable JTI revocation / single-use store. v0.47 extracts the
in-memory
Mutex<HashMap>that was hard-coded into [tenancy::jwt_lifecycle::JwtLifecycle] and the impersonation handoff into ajti_store::JtiStoretrait so multi-instance deployments can plug in Redis / DB-backed stores. Pluggable JTI (JWT ID) revocation / single-use store. - jwt
- Standalone HS256 JWT — sign / verify / decode for magic links,
microservice tokens, third-party SSO. See
jwt::encode+jwt::decode. Minimal JWT (HS256) — sign, verify, decode. - list_
params - Shared list-endpoint query-parameter helpers — single source of
truth for the reserved-key skip list,
?ordering=parsing, and?page_size=clamping. Used by bothviewset::handle_listand thetemplate_views::ListViewCBV. Closes the drift hazard flagged in issue #809. Shared helpers for list-endpoint query-parameter parsing. - logging
- One-call tracing-subscriber setup. See
logging::setup/logging::Setup. Tracing-subscriber setup helpers — the boilerplate every rustango app writes by hand becomes one call. - lorem
- Django-shape lorem ipsum generators — placeholder text for
demos, test fixtures, template scaffolds.
lorem::words(n),lorem::paragraphs(n),lorem::sentence(). Django-shape lorem ipsum placeholder text — mirrorsdjango.utils.lorem_ipsum. - mailable
- Laravel-shape
Mailabletrait — declare an email type as a struct that owns its template + recipient logic. Pairs withemail_templates::EmailRenderer(rendering) andemail_jobs(off-request delivery). Seemailable::Mailable. Laravel-shapeMailabletrait — declare an email type as a struct that owns its own template + recipient logic. Pairs withcrate::email_templates::EmailRenderer(template rendering) andcrate::email_jobs(off-request delivery). - maintenance
- Maintenance-mode middleware — return 503 with
Retry-Afterfrom a shared atomic flag, so an orchestrator can drain traffic before a deploy / migration without killing in-flight requests. Seemaintenance::MaintenanceFlag+maintenance::MaintenanceLayer. Maintenance-mode middleware — return 503 withRetry-Afterfrom a shared flag, so an orchestrator (or a sidecar) can drain traffic before a deploy / migration without killing in-flight requests. - manage
- Unified manage runner — collapses
src/main.rs+src/bin/manage.rsboilerplate into onemanage::Clibuilder. Tenancy variant available when thetenancyfeature is on. Behind themanagefeature (default-on) which pulls a minimal axum + tokio surface so bare-API projects (no admin) can still use the dispatcher. Unified manage runner — collapsessrc/main.rs+src/bin/manage.rsboilerplate into one builder so apps stop hand-writing the dispatcher. - manage_
interactive - TTY-gated interactive prompts for
manageverbs —ask(prompt)reads a line,ask_password(prompt)reads a password without echoing. Both returnOk(None)on non-TTY stdin so scripted callers keep the existing “missing required flag” error path. - manager
- Custom Manager / QuerySet-extension pattern — Django’s
class PublishedManager(Manager)/QuerySet.as_manager()adapted to Rust’s extension-trait idiom. Seemanagerfor the canonical worked examples. Issue #52. Custom Manager / QuerySet-extension pattern — Django’sclass PublishedManager(Manager)andQuerySet.as_manager()adapted to Rust. Issue #52. - media
- First-class
Mediamodel — Postgres-backed file references with direct-browser upload, CDN-aware URLs, soft delete + orphan purge. Seemedia::Media+media::MediaManager. First-classMediamodel — a real#[derive(Model)]row that references a file in thecrate::storage::Storagelayer. - messages
- Django messages framework —
messages.success/info/warning/error/debugflash storage backed by a signed cookie. Issue #9. Django’s messages framework, ported to a cookie-backed signed-storage shape. Issue #9. - method_
override - HTTP method override — rewrite POST → PUT/PATCH/DELETE based on
X-HTTP-Method-Overrideheader or_methodform field, so HTML forms can drive REST routes. Seemethod_override::MethodOverrideLayer. HTTP method override — rewrite POST → PUT / PATCH / DELETE based on a hidden form field or header. - metrics
- Prometheus-format metrics — counters + histograms exposed at
/metrics. Pure-Rust, no Prometheus client crate. Seemetrics::MetricsRegistry+metrics::metrics_router. Prometheus-format metrics — counters + histograms exposed at/metrics. - migrate
- Migrations for rustango.
- notifications
- Multi-channel notifications — fan one notification out to mail / database /
log / broadcast channels. See
notifications::notify. Multi-channel notifications — fan one notification out to mail / database / log / broadcast channels. - numberformat
- Django-shape number formatter —
numberformat::format(value, decimal_sep, decimal_pos, grouping, thousand_sep). Mirrorsdjango.utils.numberformat.format. Tri-byte grouping (thousands), per-locale decimal/thousand separator override, optional fixed decimal-place width. Django-shape number formatter — mirrorsdjango.utils.numberformat.format. - oauth2
- OAuth2 / OIDC swiss-knife — social login that works for both pure
OAuth2 (GitHub, Discord) and OIDC (Google, Microsoft, Keycloak)
providers via the
/userinfoendpoint. Per-tenant viaoauth2::OAuth2Registry. Optional axum router underoauth2::router(requires theadminfeature). OAuth2 / OIDC swiss-knife — one type, every provider. - openapi
- OpenAPI 3.1 spec builder + Swagger UI / Redoc viewer routes.
Hand-build a spec with
openapi::OpenApiSpecand mount it underopenapi::router::openapi_router(requires theadminfeature). OpenAPI 3.1 spec builder + optional Swagger UI router. - pagination
- Pagination helpers — RFC 5988 Link headers + cursor params.
See
pagination::LinkHeaderBuilder. Pagination helpers — three shapes for different surfaces. - password_
hashers - Pluggable
PASSWORD_HASHERSchain with upgrade-on-login — register an ordered list ofpassword_hashers::PasswordHashers; the first is preferred for new hashes, every entry verifies its own format, and successful logins against an older hasher return a freshly-rehashed value so the caller can transparently upgrade the stored hash. Issue #54 (partial). Pluggable password-hasher chain with upgrade-on-login — Django’sPASSWORD_HASHERS = [...]setting. - password_
validators - Pluggable
AUTH_PASSWORD_VALIDATORSchain — run an ordered list ofpassword_validators::PasswordValidators at signup / password-change. Built-ins:MinimumLengthValidator,MaximumLengthValidator,NumericPasswordValidator,UserAttributeSimilarityValidator,CommonPasswordValidator. Issue #54 (partial). Pluggable password validator chain — Django’sAUTH_PASSWORD_VALIDATORS = [...]setting. - passwords
- Generic password hash/verify + strength heuristic. See
passwords::hash. Generic password hashing + strength checking. - problem_
details - RFC 7807 “Problem Details for HTTP APIs” — standardized error
responses with
application/problem+json. Sister toapi_errors. Seeproblem_details::ProblemDetails. RFC 7807 “Problem Details for HTTP APIs” — standardized error responses with the canonicalapplication/problem+jsoncontent type. - prunable
- Model pruning — declarative bulk-removal of stale rows.
Eloquent
Prunableparity. Seeprunable::Prunable+register_prunable!. Model pruning — EloquentPrunable/ Django “scheduled bulk removal” parity. Issue #822. - query
- Query layer for rustango.
- random
- Django-shape random-string helpers —
get_random_string,get_random_token_urlsafe. CSPRNG-backed, suitable for session IDs, reset tokens, verification codes. Django-shape random-string helpers —get_random_string/get_random_token_urlsafe. - rate_
limit - Token-bucket rate limiting middleware —
rate_limit::RateLimitLayer. Per-IP, per-header, or global. Returns 429 withRetry-Afterwhen exhausted. Token-bucket rate limiting middleware for axum routers. - rate_
limit_ cache - Cache-backed rate limiting middleware — fixed-window counter via the
cache::Cachetrait. Pair withRedisCachefor distributed enforcement across multiple processes / replicas. Seerate_limit_cache::CacheRateLimitLayer. Distributed rate limiting via theCachetrait. - real_ip
- Real-IP extraction for apps behind a trusted reverse proxy
(Cloudflare / nginx / ELB / etc). Parses
X-Forwarded-For,X-Real-IP,CF-Connecting-IP, or RFC 7239Forwardedand stuffs the resolved client IP into request extensions. Seereal_ip::RealIpLayer. Real-IP extraction middleware for apps behind a trusted reverse proxy. - redirects
- Table-driven HTTP redirects —
django.contrib.redirects. Build aredirects::RedirectMap(programmatically or from a CSV) and mountredirects::redirects_middlewareon your axum router; matching requests short-circuit with a 301/302 response and the canonical URL inLocation, query strings preserved. Issue #57. Table-driven HTTP redirects —django.contrib.redirects. - request_
id - Request ID middleware — assign per-request correlation IDs,
honor inbound
X-Request-Idfor end-to-end propagation. Seerequest_id::RequestIdLayer. Request ID middleware — assign a unique ID to every incoming request. - request_
timeout - Per-request handler timeout — kills handlers that exceed the
configured duration and returns
504 Gateway Timeout. Caps the blast radius of wedged DB queries / external HTTP calls. Wired fromSettings.server.request_timeout_secsautomatically byCli::with_settings_from_env(). Seerequest_timeout::RequestTimeoutLayer. Per-request timeout middleware. Wraps an axum router so any handler that takes longer than the configured duration gets killed and the client receives504 Gateway Timeoutinstead of a hung connection. - scheduler
- In-process scheduled task runner — fire async jobs at fixed intervals.
See
scheduler::Scheduler. In-process scheduled task runner — fire async jobs at fixed intervals. - secrets
- Pluggable secrets backend —
secrets::Secretstrait +secrets::EnvSecrets - security_
headers - Security headers middleware — HSTS / X-Frame-Options / nosniff /
Referrer-Policy / Permissions-Policy / CSP. See
security_headers::SecurityHeadersLayer. Security headers middleware — HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Cross-Origin-Opener-Policy, and a Content-Security-Policy builder. - serializer
- DRF-style serializer layer —
#[derive(Serializer)]+serializer::ModelSerializer. Typed JSON output from model instances with field control and validation. DRF-style serializer layer — typed JSON output from model instances. - server
- Django-style runserver — [
server::Builder] owns every line of boilerplate every tenancy app would otherwise rewrite (DB pool, resolver chain, host dispatch, operator console, bind + serve). - server_
timing Server-Timingheader middleware — surface per-request stage durations to the browser DevTools “Network” panel. Seeserver_timing::ServerTimingLayer+server_timing::Timings.Server-Timingheader middleware — surface per-request stage durations to the browser DevTools “Network → Timing” panel.- session
- Signed-cookie session primitives — HMAC-SHA256 key wrapper +
sign(secret, msg)helper. Shared by every layer that ships a signed cookie ([tenancy::session],admin::session) so the crypto lives in one place. Seesession::SessionSecret. - sessions
- Server-side sessions backed by
cache::Cache— opaque cookie ID, server-stored bag of typed values, revocable per-session. Seesessions::Session+sessions::SessionStore. Server-side session store backed bycrate::cache::Cache. - shortcuts
- Django-shape view shortcuts —
get_object_or_404/get_list_or_404/render/redirect. Seeshortcuts. Issue #10. Django-shape view shortcuts. Issue #10. - signals
- Django-shape model signals —
signals::connect_post_saveetc. Receivers register globally per model type and run sequentially. Django-shape model signals —pre_save,post_save,pre_delete,post_delete. - signed_
url - Signed URL helpers — HMAC-SHA256 with optional expiry.
See
signed_url::sign/signed_url::verify. Signed URL helpers — tamper-evident URLs with optional expiry. - signing
- Django-shape generic value signer —
signing::Signer::sign(value) - sitemaps
- XML sitemap rendering —
django.contrib.sitemaps. Build aSitemapimpl (or pass aVec<SitemapEntry>directly) and callsitemaps::render_sitemap; for large sites usesitemaps::render_sitemap_indexto point crawlers at multiple child sitemaps. Issue #57. XML sitemap rendering — Django’sdjango.contrib.sitemaps. - soft_
delete - Soft-delete query helpers —
active_filter/trashed_filter/compose_with_active/soft_delete/restore/purgefor any model carrying#[rustango(soft_delete)]. Seesoft_delete. - sql
- SQL compilation and execution for rustango.
- sse
- Broadcast event bus — fan-out for SSE / WebSocket / signal-driven push.
See
sse::EventBus. Broadcast event bus — fan one message out to every connected subscriber. The foundation for Server-Sent Events (SSE) and other real-time push patterns. - ssl_
redirect - HTTP → HTTPS redirect middleware — Django
SECURE_SSL_REDIRECT - static_
files - Static file serving — read files from a directory with sensible
Content-Type,Cache-Control,Last-Modified, and 304 support. Seestatic_files::StaticFiles+static_files::static_router. Static file serving — read files from a directory and ship them with sensibleContent-Type,Cache-Control, andLast-Modifiedheaders, plusIf-Modified-Since304 support. - storage
- File storage backends —
storage::Storagetrait + LocalStorage + InMemoryStorage. File storage backends — upload, retrieve, delete files via a pluggable trait. - syndication
- RSS 2.0 + Atom 1.0 feed rendering —
django.contrib.syndication. Build asyndication::Feedwith channel metadata +syndication::FeedItems and callsyndication::render_rssorsyndication::render_atom. Issue #57. RSS 2.0 + Atom 1.0 feed rendering —django.contrib.syndication. - template_
context_ processors - Django-shape template context processors — see
template_context_processors. Issue #384. Django-shape template context processors — issue #384. - template_
debug - Django-shape DEBUG template-error overlay — see
template_debug. Issue #386. Django-shape DEBUG template-error overlay — issue #386. - template_
extensions - Django-shape custom template filters + functions — see
template_extensions. Issue #383. Django-shape custom template tags + filters — issue #383. - template_
views - Generic class-based views for HTML templates (Django-shape) —
ListView,DetailView,CreateView,UpdateView,DeleteViewover#[derive(Model)]schemas, rendered through Tera. Sibling ofviewsetfor the JSON/API side. Seetemplate_viewsfor usage and the canonical Tera context shape. - test_
assertions - Django-shape test assertion helpers —
assert_contains/assert_redirects/assert_status/assert_messageson axum Response objects. Issue #40. Django-shape assertion helpers for axum response objects. Issue #40. - test_
client - Test client — fire HTTP requests against an
axum::Routerin tests without binding a real socket. Seetest_client::TestClient. Test client — fire HTTP requests against anaxum::Routerin tests without binding a real socket. - test_
data - Class-level test fixtures — Django’s
setUpTestData(cls). Wrapsstd::sync::OnceLock/tokio::sync::OnceCellwith thesetup_test_data!/setup_test_data_async!macros so test fixtures lazily init once per test-binary process. Issue #42. Class-level test fixtures — Django’ssetUpTestData(cls). - test_db
- Test-time DB isolation — Django’s
TestCasetransaction wrapping.test_db::with_rollbackruns an async closure inside a transaction that always rolls back, so test mutations don’t leak into the next test. Issue #39 partial. Test-time DB isolation — Django’sTestCase/TransactionTestCaseanalogs. Issue #39. - test_
factory - Django-shape test factories —
factory_boyparity.test_factory::Sequencecounter +test_factory::Factorytrait withbuild_batchfor declarative model builders. Issue #432. Django-shape test factories —factory_boyparity. - test_
filter - Tag-based test filtering — Django’s
@tag('slow')+manage test --tag fast --exclude-tag slow. Droptags!("slow")at the top of a#[test]body; the test self-skips when theRUSTANGO_TEST_TAGS/RUSTANGO_TEST_EXCLUDE_TAGSenv vars filter it out. Issue #45. Tag-based test filtering — Django’s@tag('slow', 'core')+manage test --tag fast --exclude-tag slow. Issue #45. - test_
server - Live HTTP server for tests — Django’s
LiveServerTestCase. Bindsaxum::Routerto a random localhost TCP port and runs it on a background task. Use when in-processtest_client::TestClientrouting isn’t enough (Selenium, websockets, code reading scheme/host headers). Issue #39 partial. Live HTTP server for tests — Django’sLiveServerTestCase. - test_
settings - Test-only Settings overlay — Django’s
@override_settings/with self.settings(...). Wraps atokio::task_local!config::Settingsoverlay so test code can install per-task configuration without touching process-global state.test_settings::currentreads through the overlay → fallback chain. Issue #43. Test-only Settings overlay — Django’s@override_settings/with self.settings(...). Issue #43. - text
- Text utilities — slugify, html_escape, truncate. Text utilities — slug generation, HTML escaping, truncation.
- timesince
django.utils.timesinceparity —timesince/timeuntilplain-function API with Django-styledepthcontrol (“4 days, 6 hours”). Public Rust surface; Tera filter wrappers live inhumanize. Djangodjango.utils.timesinceparity — human duration strings.- totp
- TOTP — RFC 6238 time-based one-time passwords for 2FA.
See
totp::generate/totp::verify/totp::otpauth_url. TOTP — Time-based One-Time Passwords (RFC 6238) for 2FA. - tracing_
layer - Per-request
tracingspan with W3C / OpenTelemetry-conventional field names +traceparentheader propagation. Hooktracing_opentelemetry::layer()in your subscriber for free distributed tracing. Seetracing_layer::TracingLayer. Per-requesttracingspan with W3C / OpenTelemetry-conventional field names +traceparentheader propagation. - trailing_
slash - Trailing-slash redirect middleware — canonicalize URL paths
(Django
APPEND_SLASH/ Railstrailing_slashshape). Seetrailing_slash::TrailingSlashLayer. Trailing-slash redirect middleware — canonicalize URL paths. - uploads
- Multipart file upload helper — wraps axum’s multipart extractor +
the
storage::Storagetrait. Seeuploads::save_uploads+uploads::UploadConfig. Multipart file upload helper — wraps axum’s multipart extractor + thecrate::storage::Storagetrait so a file upload handler is a one-liner instead of buffering / size-checking / extension- validating glue. - url_
codec - Django
django.utils.encoding+django.utils.httpURL/IRI/URI helpers —url_encode,uri_to_iri,iri_to_uri,escape_uri_path,filepath_to_uri,urlsafe_base64_encode/_decode, etc. Originally an internalpub(crate)module used bysigned_url,auth_flows, and [tenancy::admin]; promoted topubso consumer crates can reach the Django-parity helpers directly (otherwise theuri_to_iri/filepath_to_uri/escape_uri_path/is_uri_reservedhelpers shipped by thedjango.utilsparity sweep would be unreachable). URL codec helpers —application/x-www-form-urlencodedpercent decoder + RFC-3986 percent encoder + Django-shape urlsafe base64. - urls
- Named URL reversal — Django’s
reverse(name, params)+get_absolute_url(). Pair withregister_url!. Issue #8. Named URL reversal — Django’sreverse()+get_absolute_url(). Issue #8. - validators
- Django-shape standalone validators —
validate_email,validate_url,validate_slug,validate_min_length/validate_max_length,validate_min_value/validate_max_value. Seevalidators. Issue #54. Django-shape standalone validators. Issue #54. - viewset
- DRF-style ModelViewSet — five REST endpoints for any
Modeltable in ~5 lines. Seeviewset::ViewSet. - webhook
- Webhook signature verification (HMAC-SHA256). See
webhook::verify_signature. Webhook signature verification — HMAC-based, constant-time. - webhook_
delivery - Outbound webhook delivery — POSTs HMAC-signed JSON via the background
job queue (so retries with exponential backoff are free). See
webhook_delivery::WebhookSubscription. Outbound webhook delivery — POSTs an HMAC-signed JSON payload to a subscriber URL via the background job queue. - welcome
- First-run welcome page — confidence signal that rustango is wired up.
Mount under
/while bootstrapping; replace once you have content. Seewelcome::welcome_router. First-run welcome page — confidence signal that rustango is wired up. - ws
- WebSocket handler scaffold — fan-out via
sse::EventBuswith auto JSON encode/decode + keep-alive ping. Seews::WsHub+ws::ws_handler. WebSocket handler scaffold — fan-out via the SSE [EventBus].
Macros§
- Q
Q!()— Django-shape filter syntax compile-time-resolved against typed columns. Each invocation expands to the equivalent typed-column method call, so field-name typos fail the build. Seerustango_macros::Qfor the supported lookup suffixes.- atomic
- Sugar over
atomicthat wraps the body inBox::pin(async move { … })so callers don’t have to. Identical semantics: - embed_
migrations - Bake every migration file in a directory into the binary at
compile time, for shipping a single-binary distribution. Pair
with
migrate::migrate_embedded. Bake every*.jsonmigration file in a directory into the binary at compile time. Returns a&'static [(&'static str, &'static str)]of(name, json_content)pairs, lex-sorted by file stem. - register_
admin_ computed - Register an admin computed field. Pair with a
#[derive(Model)]type whoseadmin(list_display = "…")names this field. - register_
admin_ inline - Register an admin inline. The
parent/childarguments must match theModelSchema::tableof two#[derive(Model)]types already in the registry. - register_
admin_ inline_ generic - Register a generic admin inline. Same shape as
[
register_admin_inline!] but takesct+pkinstead offk. - register_
admin_ list_ filter - Register a custom list filter. Pair with the table whose admin list view should expose the filter card.
- register_
admin_ object_ permission - Register a Django-shape
has_<action>_permissionpredicate scoped to one model. - register_
admin_ queryset - Register an admin queryset hook scoped to one model.
- register_
admin_ view - Register a custom admin view scoped to one model.
- register_
migration_ callback - Register a named migration callback. Pair the chosen name with a
{"callback": {"name": "..."}}entry in your migration JSON’sforwardarray. - register_
prunable - Register a
Prunableimpl with the inventory walker. Themanage pruneCLI verb walks every entry on each run; the in-processprune_all/prune_pretendhelpers do the same. - register_
template_ context_ processor - Register a template context processor. Pair with
apply_to_contextat the handler site (or rely on the framework’s CBV wrappers, which call it automatically). - register_
template_ filter - Register a custom Tera filter globally. Picked up by
apply_to_teraat template-engine construction time. - register_
template_ function - Register a custom Tera function globally. Picked up by
apply_to_teraat template-engine construction time. - register_
url - Register a named URL pattern at module-load time. Picks up
rustango::inventoryso callers don’t need their own dep. - setup_
test_ data - Class-level synchronous test fixture — Django’s
setUpTestDatafor fixtures that don’t need an async runtime. Wraps astd::sync::OnceLockin a function-call surface. - setup_
test_ data_ async - Class-level async test fixture — Django’s
setUpTestDatafor fixtures that need to hit the database (the common case for model-row fixtures). Wraps atokio::sync::OnceCellso concurrent first-access calls all wait on the same future, with only one actually running the init body. - tags
- Declare this test’s tags and early-return if the current
RUSTANGO_TEST_TAGS/RUSTANGO_TEST_EXCLUDE_TAGSenv vars say it shouldn’t run. Place at the top of the test body — before any setup that you don’t want to run for filtered-out tests. - with_
rollback - Sugar around
with_rollbackthat wraps the body inBox::pin(async move { … })so callers don’t have to. Identical semantics: - with_
truncate_ after - Sugar around
with_truncate_aftermirroring thewith_rollback!macro shape:
Enums§
- Rustango
Error - Unified error type for app-level code.
Fromimpls cover every module in the framework so?Just Works in handlers.
Type Aliases§
- Rustango
Result - Standard alias.
Attribute Macros§
- main
#[rustango::main]— the Django-shaperunserverentrypoint. Wraps#[tokio::main]with a defaulttracing-subscriberboot (env-filter, falling back toinfo,sqlx=warn). Available behind theruntimefeature, whichtenancyimplies.#[rustango::main]— the Django-shape runserver entrypoint. Wraps#[tokio::main]and a defaulttracing_subscriberinitialisation (env-filter, falling back toinfo,sqlx=warn) so usermainfunctions are zero-boilerplate:
Derive Macros§
- Form
#[derive(Form)]— implementsforms::Formso a struct can be parsed from an HTTP form payload with multi-error validation. Re-exported only when theformsfeature is on. Deriverustango::forms::Form(slice 8.4B). Generates aparse(&HashMap<String, String>) -> Result<Self, FormErrors>impl that walks every named field and:- Model
#[derive(Model)]— populates theinventoryregistry the admin walks, generatesobjects()/ typed columns /insert/delete/save. Derive aModelimpl. See crate docs for the supported attributes.- Serializer
#[derive(Serializer)]— implementsserializer::ModelSerializeron a struct, generatingfrom_model, a customserde::Serialize(respectingwrite_only), andwritable_fields. Re-exported when theserializerfeature is on. Deriverustango::serializer::ModelSerializerfor a struct. (intra-doc link disabled — the macro crate doesn’t depend onrustangoitself, so rustdoc can’t resolve the path.)- ViewSet
#[derive(ViewSet)]— generates arouter(prefix, pool) -> axum::Routerassociated method on a marker struct, wiring the full CRUD ViewSet in one annotation. Re-exported underadminortenancy(the generatedrouter(...)expands to__axum::Router, whichadminprovides); the generatedrouter(...)works without tenancy, andtenant_router(...)adds the multi-tenant path whentenancyis also enabled. Derive arouter(prefix, pool) -> axum::Routerassociated method on a marker struct, wiring the full CRUD ViewSet in one annotation.