#postgresql #orm #sql-database #async-orm #sql #async

mini-query

一个轻量级的 ORM,用于使用 tokio-postgres 快速查找/插入/检索记录。

7 个版本

0.1.6 2023 年 12 月 13 日
0.1.5 2023 年 12 月 13 日

#15#async-orm

40 每月下载量

MPL-2.0 许可证

8KB
61

Mini Query

Latest Version

一个轻量级的 ORM,用于使用 tokio-postgres 快速查找/插入/检索记录。

在结构体上生成以下函数

如果设置了 #[mini_query(primary_key)]

get(id: &T) -> Result<Option<T>>

对于所有标记了 #[mini_query(find_by)] 的字段

find_by_{x}(client: &impl GenericClient, val: &T) -> Result<Option<T>>

对于所有标记了 #[mini_query(get_by)] 的字段

get_by_{x}(client: &impl GenericClient, val: &T) -> Result<Vec<T>>

当然,也支持插入和更新。

quick_insert(&self, client: &impl GenericClient) -> Result<Self>
quick_insert_no_return(&self, client: &impl GenericClient) -> Result<()>
quick_update(&self, client: &impl GenericClient) -> Result<Self>

此宏还为您结构体实现了 From 特性。这使得以下成为可能

let user: User = client.query_one("SELECT * FROM users WHERE id = $1", &[&1]).await?.into();

以下是一个 "users" 表的示例

use chrono::prelude::*;
use mini_query::MiniQuery;
use tokio_postgres::GenericClient;

#[derive(MiniQuery, Default)]
#[mini_query(table_name = "users")]
struct User {
  #[mini_query(primary_key)]
  pub id: i32,
  #[mini_query(find_by)]
  pub email: String,
  #[mini_query(skip)]
  pub raw_password: Option<String>,
  #[mini_query(rename = "password")] // renames field to "password" when saving
  pub enc_password: String,
  #[mini_query(cast = i16, get_by)] // field is represented by a smallint in postgres
  pub role: UserRole,
  pub created_at: DateTime<Utc>,
  pub updated_at: DateTime<Utc>,
}
impl User {
  fn encrypt_password(&mut self) {
    let Some(raw_password) = &self.raw_password else {
      return;
    }
    self.enc_password = format!("{raw_password} - tada, I am encrypted");
  }
}

#[derive(Default, Clone, Copy)]
#[repr(i16)]
enum UserRole {
  #[default]
  User = 0,
  Admin = 1
}
impl From<i16> for UserRole {
  fn from(val: i16) -> Self {
    match val {
      0 => UserRole::User,
      1 => UserRole::Admin,
      _ => unimplemented!(),
    }
  }
}

#[derive(MiniQuery, Default)]
#[mini_query(table_name = "posts")]
struct Post {
  #[mini_query(primary_key)]
  pub user_id: i32,
  pub content: String
}
impl Post {
  belongs_to!(User, user, user_id);
}

#[tokio::main]
async fn main() -> Result<()> {
  let (client, connection) =
        tokio_postgres::connect("postgresql://postgres@localhost/mydb-dev", NoTls).await?;
    tokio::spawn(async move { connection.await });

    let mut user = User {
        email: "[email protected]".to_owned(),
        raw_password: Some("I am bad password".to_owned()),
        role: UserRole::Admin,
        ..Default::default()
    };
    user.encrypt_password();

    // fn is prefixed with "quick_" to avoid naming collisions',
    // in case you wish to write your own validation wrapper.
    user.quick_insert(&client).await?;

    // find user by email
    let same_user = User::find_by_email(&client, "[email protected]").await?.unwrap();
    assert_eq!(user.email, same_user.email);

    // get all the admins
    let admin = User::get_by_role(&client, &UserRole::Admin)
        .await?
        .pop()
        .unwrap();
    assert_eq!(same_user.email, admin.email);

    // get user by id and update
    let mut user = User::get(&client, &same_user.id).await?.unwrap();
    user.email = "[email protected]".to_owned();
    user.quick_update(&client).await?;

    // assert it saved
    assert_eq!(
        &User::get(&client, &user.id).await?.unwrap().email,
        "[email protected]"
    );

  Ok(())
}

目前仅支持 tokio-postgres

依赖关系

~0.8–1.3MB
~25K SLoC