Rust 2026-07-10·Ievgenii Svyryd

Rustango: przenosimy baterie Django do Rusta

Framework webowy z pełnym wyposażeniem, który przenosi doświadczenie deweloperskie Django - typowany ORM, migracje, panel admina, uwierzytelnianie i polecenia zarządzające - do asynchronicznego Rusta, bez rezygnowania z gwarancji na etapie kompilacji.

Rustango: przenosimy baterie Django do Rusta

Uwielbiam Django. Po latach dostarczania z jego pomocą produktów filozofia tego frameworka wrosła w mój sposób myślenia: typowana warstwa modeli, migracje, które piszą się same, panel administracyjny, który dostajesz za darmo, oraz CLI zarządzające, które spina to wszystko w całość. Uwielbiam też Rust - kompilator, który nie pozwala, aby cała klasa błędów trafiła na produkcję, oraz środowisko uruchomieniowe, które nie potrzebuje garbage collectora ani niańki.

Przez długi czas te dwie miłości żyły w oddzielnych światach. Rustango to moja próba połączenia ich razem: framework webowy z bateriami w zestawie, który przenosi doświadczenie programistyczne Django do asynchronicznego Rusta - bez rezygnowania z gwarancji na etapie kompilacji.

Typowany ORM, który czyta się jak Django

Sercem każdego frameworka w stylu Django jest warstwa modelu. W Rustango wyprowadzasz Model na zwykłej strukturze i adnotujesz ją tabelą, kluczem głównym oraz ograniczeniami kolumn. Makro derive generuje typowany zestaw kolumn oraz łańcuchowalny QuerySet - dzięki czemu builder zapytań jest sprawdzany przez kompilator, a nie odkrywany w czasie wykonania.

#[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ńcuchowalny, sprawdzany typowo QuerySet - nazwy kolumn to prawdziwe symbole.
let recent = Post::objects()
    .where_(Post::published.eq(true))
    .order_by(&[("created_at", false)])
    .fetch(pool)
    .await?;

Nie ma tekstowo typowanego DSL zapytań, w którym można zrobić literówkę. Post::published to wygenerowany symbol; wpisz go błędnie, a kompilacja się nie powiedzie. Te same metadane kolumn napędzają wstawianie, aktualizacje oraz różnicę schematu, którą pochłania silnik migracji.

Migracje, wygenerowane i zastosowane

Modele Rustango niosą SCHEMA, który framework może porównać z bazą danych. CLI generuje pliki migracji z różnicy i stosuje je per baza danych - a ponieważ Rustango jest wielodzierżawcowy od podstaw, zna różnicę między współdzielonym schematem rejestru a schematem każdego dzierżawcy.

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

Panel administracyjny, którego nie musiałeś pisać

Wystarczy opatrzyć model blokiem admin(...) - kolumny listy, sortowanie, wyszukiwanie - a Rustango wyrenderuje dla niego pełny panel administracyjny CRUD: widoki list z filtrami i paginacją, formularze tworzenia/edycji generowane na podstawie widżetów pól oraz akcje zbiorcze. To jedyna funkcja, która sprawia, że Django przypomina cheat code, i której najbardziej brakowało mi w Ruście.

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

Wbudowane funkcje: uwierzytelnianie, sesje, formularze, CSRF, buforowanie

Framework zasługuje na określenie batteries-included dzięki nudnym rzeczom, które dostarcza, byś nigdy więcej nie musiał ich pisać. Rustango wnosi podpisane sesje, stos haseł/przepływów uwierzytelniania, warstwę formularzy z ochroną CSRF, cache (w tym cache'owanie całych stron), sygnały, podpisane URL-e oraz manage::Cli, które łączy routing, wielodostępność (tenancy), migracje i Twoje własne polecenia w jeden plik binarny. Routing opiera się na Axum i Tokio, więc historia asynchroniczności jest tą dojrzałą, której ekosystem Rusta już ufa.

Obietnicą Django nigdy nie była składnia — chodziło o to, że framework już podjął za Ciebie nudne decyzje. Rustango dotrzymuje tej obietnicy, a kompilator trzyma Cię w ryzach.

Czy było warto?

Portowanie frameworka nie jest weekendowym projektem, a Rust wymaga więcej na starcie niż Python. Ale zwrot jest realny: ta sama definicja modelu napędza bazę danych, panel administracyjny i API, a borrow checker zamienia całą kategorię incydentów produkcyjnych w błędy kompilacji. Jeśli kiedykolwiek marzyłeś, aby Django działało z prędkością Rusta i z gwarancjami Rusta, Rustango jest tym marzeniem zapisanym w kodzie.

Kontakt →