Rust 2026-07-10·Ievgenii Svyryd

Rustango : apporter les fonctionnalités intégrées de Django à Rust

Un framework web tout-en-un qui porte l'expérience développeur de Django — l'ORM typé, les migrations, l'admin, l'authentification et les commandes de gestion — vers du Rust asynchrone, sans renoncer aux garanties à la compilation.

Rustango : apporter les fonctionnalités intégrées de Django à Rust

J'adore Django. Après des années à livrer des produits avec ce framework, sa philosophie est ancrée dans ma façon de penser : une couche de modèles typée, des migrations qui s'écrivent toutes seules, une interface d'administration offerte gratuitement, et une CLI de gestion qui relie le tout. J'adore aussi Rust - le compilateur qui refuse de laisser toute une catégorie de bugs atteindre la production, et le runtime qui n'a besoin ni d'un ramasse-miettes ni d'une surveillance constante.

Pendant longtemps, ces deux amours ont vécu dans des mondes séparés. Rustango est ma tentative de les réunir : un framework web tout-en-un qui porte l'expérience développeur de Django vers du Rust asynchrone - sans renoncer aux garanties à la compilation.

Un ORM typé qui se lit comme du Django

Le cœur de tout framework à la Django est la couche de modèles. Dans Rustango, vous dérivez Model sur une struct ordinaire et vous l'annotez avec la table, la clé primaire et les contraintes de colonnes. La macro de dérivation génère un jeu de colonnes typé et un QuerySet chaînable - de sorte que le constructeur de requêtes est vérifié par le compilateur, et non découvert à l'exécution.

#[derive(Model, Default, Serialize, Deserialize)]
#[rustango(table = "blog_post", app = "blog")]
pub struct Post {
    #[rustango(primary_key)]
    pub id: Auto<i64>,
    #[rustango(max_length = 200)]
    pub title: String,
    pub published: bool,
    #[rustango(auto_now_add)]
    pub created_at: Auto<DateTime<Utc>>,
}

// A chainable, type-checked QuerySet - column names are real symbols.
let recent = Post::objects()
    .where_(Post::published.eq(true))
    .order_by(&[("created_at", false)])
    .fetch(pool)
    .await?;

Il n'y a aucun DSL de requête typé sous forme de chaînes où faire une faute de frappe. Post::published est un symbole généré ; écrivez-le mal et la compilation échoue. Les mêmes métadonnées de colonne alimentent les insertions, les mises à jour et le diff de schéma que consomme le moteur de migration.

Migrations, générées et appliquées

Les modèles Rustango portent un SCHEMA que le framework peut comparer à la base de données. La CLI génère des fichiers de migration à partir du diff et les applique par base de données - et comme Rustango est multi-tenant dès la conception, il fait la différence entre le schéma de registre partagé et le schéma de chaque tenant.

cargo run -- makemigrations      # diff models -> migration files
cargo run -- migrate-registry    # apply the shared/registry schema
cargo run -- migrate-tenants     # apply every tenant database

L'administration que vous n'avez pas eu à écrire

Annotez un modèle avec un bloc admin(...) - colonnes de liste, tri, recherche - et Rustango génère pour lui une interface d'administration CRUD complète : des vues de liste avec filtres et pagination, des formulaires de création/édition générés à partir de vos widgets de champ, et des actions groupées. C'est la fonctionnalité qui donne à Django des airs de triche, et c'est celle qui m'a le plus manqué en Rust.

#[rustango(
    table = "blog_post", app = "blog",
    admin(list_display = "title, published, created_at", ordering = "-created_at")
)]
pub struct Post { /* ... */ }

Fonctionnalités intégrées : authentification, sessions, formulaires, CSRF, mise en cache

Un framework mérite le qualificatif batteries-included par les tâches fastidieuses qu'il fournit d'emblée pour que vous n'ayez plus jamais à les écrire. Rustango apporte des sessions signées, une pile d'authentification et de flux de mot de passe, une couche de formulaires avec protection CSRF, un cache (y compris la mise en cache de pages entières), des signaux, des URL signées, et un manage::Cli qui câble le routage, la multi-tenance, les migrations et vos propres commandes dans un seul binaire. Le routage repose sur Axum et Tokio, si bien que l'histoire de l'asynchrone est celle, mature, à laquelle l'écosystème Rust fait déjà confiance.

La promesse de Django n'a jamais été la syntaxe — c'était que le framework avait déjà pris les décisions ennuyeuses à votre place. Rustango tient cette promesse, et le compilateur vous garde honnête.

Est-ce que ça en valait la peine ?

Porter un framework n'est pas un projet de week-end, et Rust exige davantage en amont que Python. Mais le gain est réel : la même définition de modèle pilote la base de données, l'admin et l'API, et le borrow checker transforme toute une catégorie d'incidents de production en erreurs de compilation. Si vous avez déjà rêvé que Django tourne à la vitesse de Rust avec les garanties de Rust, Rustango est ce à quoi ce rêve ressemble en code.

Me contacter →