Rusql Alchemy: A Django-Inspired ORM for Rust
Welcome to Rusql Alchemy! This project is a personal challenge to create a simple, intuitive, and powerful ORM for Rust, inspired by the fantastic Django ORM. While it started as a fun side project, it has grown into a capable library that I use for my own applications.
✨ Core Features
- Django-like Model Definitions: Define your database models using simple Rust structs and derive macros.
- Simple & Expressive Query API: Fetch, create, update, and delete records with an intuitive and chainable API.
- Automatic Migrations: Keep your database schema in sync with your models effortlessly.
- Multi-Database Support: Works with PostgreSQL, MySQL, SQLite, and Turso out of the box.
- Asynchronous from the Ground Up: Built with
async/.awaitfor modern, non-blocking applications.
❗️ Runtime Compatibility
This library is built on sqlx and libsql, which are designed to work with the tokio async runtime. All asynchronous operations in rusql-alchemy must be executed within a tokio runtime.
Using this library in other runtimes, such as the one provided by actix-web (#[actix_web::main]), will likely result in runtime panics. Please ensure you are using #[tokio::main] or are manually running a tokio runtime.
🚀 Getting Started
1. Add Rusql Alchemy to Your Project
Depending on the database you want to use, add one of the following to your Cargo.toml:
For PostgreSQL:
[]
= { = "0.5.7", = false, = ["postgres"] }
= "0.8"
= { = "1", = ["full"] }
For MySQL:
[]
= { = "0.5.7", = false, = ["mysql"] }
= "0.8"
= { = "1", = ["full"] }
For SQLite:
[]
= "0.5.7"
= "0.8"
= { = "1", = ["full"] }
For Turso:
[]
= { = "0.5.7", = false, = ["turso"] }
= "1.0"
= { = "1", = ["full"] }
2. Define Your Models
Create your database models using simple Rust structs and the field derive macro. The macro automatically generates the necessary code for database interactions.
When using an auto-incrementing primary key (auto=true), it is recommended to use Option<Integer> for the field type. This allows the model to represent a record that has not yet been inserted into the database (where the ID would be None).
use *;
Note for PostgreSQL users: For auto-incrementing primary keys, it's recommended to use the
Serialtype instead ofIntegerwithauto=true.
3. Connect to Your Database & Run Migrations
Instantiate the Database and run your migrations.
use *;
use Error;
async
NB: For migrations to work correctly, the models must be imported into the binary where
database.up()is called. This allows the migration system to discover your models. If your models are in a separate module (e.g.,src/models.rs), ensure you import them:// In your main.rs use *; use Error; // Import your models so they can be discovered for migration. // The `allow(unused_imports)` attribute is useful here. use crate*; // Assuming models are in `src/model` async
Database Migrations (Database.up/Database.down)
The Database struct provides up() and down() methods to manage your database schema by running registered migrations.
database.up(): Executes all registered "up" migrations, typically creating tables or applying schema changes.database.down(): Executes all registered "down" migrations, typically reverting schema changes like dropping tables.
These methods iterate through MigrationRegistrar instances, which are automatically collected when your models derive the Model trait and are imported into your binary.
Examples
Running Up Migrations:
use *;
use Error;
async
Running Down Migrations:
use *;
use Error;
async
Model Trait for Schema Management
The Model trait is central to defining your database models and includes methods for individual model schema management. When you derive #[derive(Model)] for your struct, it automatically implements the Model trait, including constants for SQL statements and default up() and down() methods specific to that model.
Model::UP: A&'static strcontaining the SQL to create the table for the specific model.Model::DOWN: A&'static strcontaining the SQL to drop the table for the specific model.Model::NAME: A&'static strrepresenting the name of the database table.Model::PK: A&'static strrepresenting the primary key column name.Model::up(conn): ExecutesModel::UPto create or alter the model's table.Model::down(conn): ExecutesModel::DOWNto drop the model's table.
Example: Individual Model Migrations
use *;
use FromRow; // Required for sqlx-based features
async
CRUD Operations
Create
use *;
use Error;
async
Read
use *;
use Error;
async
Update
use *;
use Error;
async
Delete
use *;
use Error;
async
JOIN Operations
Rusql Alchemy supports INNER JOIN operations, allowing you to query data from multiple tables at once.
First, define the models with a foreign key relationship. Assuming the User model from before, we can define a Profile model:
use *;
Then, you can use the select! macro to perform a join. The join method returns a Vec<T> where T is the type specified in join::<T>.
use *;
use Error;
// Assuming User and Profile models are defined as above
async
A Personal Challenge
This project is, first and foremost, a personal challenge and a learning experience. It's a testament to the power and flexibility of Rust, and I'm proud of how far it has come. I hope you find it useful, and I welcome any feedback or contributions from the community.