#postgresql #sqlx #orm #activerecord

sqlx-models-orm

ActiveRecord based on SQLx for Postgres

11 releases

0.7.10 Sep 11, 2024
0.7.9 Sep 11, 2024
0.7.4 Jun 10, 2024
0.1.6 Jul 17, 2022
0.1.3 May 28, 2022

#910 in Database interfaces

MIT/Apache

16KB
256 lines

Sqlx Models ORM

ActiveRecord pattern for Rust based on SQLx. Write idiomatic DB code (Postgres only).


[dependencies]
sqlx-models-orm = "0.1"

Installation

Read the in-depth tutorial that doubles as a "kitchen sink" test in the examples

These are just some of the time-saving, boilerplate-killing features:

Model

  model!{
    state: App,
    table: humans,
    struct Human {
      #[sqlx_model_hints(int4, default)]
      id: i32,
      #[sqlx_model_hints(varchar)]
      name: String,
      #[sqlx_model_hints(int4)]
      age: Option<i32>,
      #[sqlx_model_hints(boolean, default)]
      is_allowed_unlimited_cats: bool,
      #[sqlx_model_hints(boolean)]
      likes_dogs_too: bool,
    },
    has_many {
      Cat(human_id),
    }
  }

Create

  let alice = app.human()
    .insert(InsertHuman{
      name: "Alice".to_string(),
      age: Some(19),
      likes_dogs_too: true,
    })
    .save().await?;

  assert_eq!(alice.attrs, HumanAttrs{
    id: 1,
    name: "Alice".to_string(),
    age: Some(19),
    is_allowed_unlimited_cats: false,
    likes_dogs_too: true,
  });

Query

  let some_humans = app.human()
    .select()
    .limit(2)
    .offset(1)
    .likes_dogs_too_eq(false)
    .order_by(HumanOrderBy::Name)
    .desc(true)
    .all().await?;

  assert_eq!(some_humans, vec![alice]);

Update

  let updated_alice = alice.update().use_struct(UpdateHuman{
    name: Some("Alice Alison".to_string()),
    age: Some(None),
    is_allowed_unlimited_cats: Some(true),
    ..Default::default()
  }).save().await?;

  assert_eq!(updated_alice.attrs, HumanAttrs{
    id: 1,
    name: "Alice Alison".to_string(),
    age: None,
    is_allowed_unlimited_cats: true,
    likes_dogs_too: true,
  });

Delete

  alice.delete().await?;

Design principles:

  • Stateful: You're not supposed to be passing a connection pool around explicitly.
  • Your structs, your abstractions: This crate has a proc macro that creates a number of structs for different operations on a single database table. You can add any methods you want to any of these structs. Structs for the same operation in different tables implement a common trait to allow some degree of generalization across operations in these tables.
  • Idiomatic rather than performant: This should be easy to learn and use, even if there were performance tradeoffs.
  • Fallaback to SQLx: Always make it possible to fall back for custom queries and performance enhancements.
  • One table per query. Reinventing SQL for joining tables in the ORM is hard to debug and understand. Favour multiple single-table queries over a single multi-table one. (see previous item).
  • Only compile time checked queries. No chance of sql injection, no need for silly tests, at the cost of longer queries.
  • Only Postgres for now. Sorry about that :(

Dependencies

~60MB
~1M SLoC