7 releases (2 stable)

1.0.1 Nov 6, 2023
1.0.0 Sep 14, 2022
0.1.4 Jan 12, 2022
0.1.3 Nov 10, 2021
0.1.0 Aug 13, 2021

#1912 in Procedural macros


Used in diesel_filter

MIT/Apache

17KB
298 lines

Diesel Filter

Diesel filter is a quick way to add filters and pagination to your diesel models. Works with Diesel and Postgres.

Crate features

  • rocket Derives FromForm on the generated filter struct (See this example)
  • actix Derives Deserialize on the generated filter struct (See this example)
  • pagination Adds the Paginate trait (See this example)
  • serialize with pagination Adds the PaginatedPayload trait that can directly be sent to your client

Usage & Examples

Cargo.toml

diesel_filter = { path = "../../diesel_filter/core", features = ["pagination", "serialize", "rocket"] }

Derive your struct with DieselFilter and annotate the fields that will be used as filters. The top level annotation #[diesel(table_name = db_table)] is mandatory.

#[derive(Queryable, DieselFilter)]
#[diesel(table_name = projects)]
pub struct Project {
    pub id: Uuid,
    #[filter(substring, insensitive)]
    pub name: String,
    #[filter(substring)]
    pub owner_email: String,
    #[filter]
    pub owner_id: Uuid,
    pub created_at: NaiveDateTime,
}

The #[filter] annotation can receive the kinds of filter you want to apply on it, for the moment, there is only substring and insensitive.

A struct for the filtering data will be generated with the name [YourStructName]Filters, e.g: ProjectFilters. Two methods will be generated (let's keep Project as an example):

pub fn filter<'a>(filters: &'a ProjectFilters) -> BoxedQuery<'a, Pg>

and

pub fn filtered(filters: &ProjectFilters, conn: &PgConnection) -> Result<Vec<Project>, Error>

The filter method can be used in conjunction with other diesel methods like inner_join and such.

Project::filter(&filters)
    .inner_join(clients::table)
    .select((projects::id, clients::name))
    .load::<ProjectResponse>(conn)

With Rocket

With the rocket feature, the generated struct can be obtained from the request query parameters (dot notation ?filters.name=xxx)

use diesel_filter::PaginatedPayload;

#[get("/?<filters>")]
async fn index(filters: ClientFilters, conn: DbConn) -> Result<Json<PaginatedPayload<Client>>, Error> {
    Ok(Json(
        conn.run(move |conn| Client::filtered(&filters, conn))
            .await?
            .into(),
    ))
}

With Actix

With the actix feature, the generated struct can be obtained from the request query parameters

N.B: unlike the rocket integration, the query parameters must be sent unscopped. e.g ?field=xxx&other=1

use diesel_filter::PaginatedPayload;

#[get("/")]
async fn index(filters: web::Query(ClientFilters), conn: DbConn) -> Result<Json<PaginatedPayload<Client>>, Error> {
    Ok(Json(
        conn.run(move |conn| Client::filtered(&filters, conn))
            .await?
            .into(),
    ))
}

With Pagination

With the pagination feature, you have access to the methods paginate, per_page and load_and_count

use diesel_filter::Paginate;

Project::filter(&filters)
    .inner_join(clients::table)
    .select((projects::id, clients::name))
    .paginate(filters.page)
    .per_page(filters.per_page)
    .load_and_count::<ProjectResponse>(conn)

These are independent of the #[pagination] annotation that you can add on your struct to add page and per_page to your generated filter struct and change the signature of the filtered method.

#[derive(Queryable, DieselFilter)]
#[diesel(table_name = projects)]
#[pagination]
pub struct Project

To convert this into Json, with the feature flag serialize you can use PaginatedPayload.

pub struct PaginatedPayload<T> {
    data: Vec<T>,
    total: i64,
}
#[get("/?<filters>")]
async fn index(filters: ProjectFilters, conn: DbConn) -> Result<Json<PaginatedPayload<Project>>, Error> {
    Ok(Json(
        conn.run(move |conn| Project::filtered(&filters))
        .await
        .into(),
    ))
}

License

Diesel filter is licensed under either of the following, at your option:

Dependencies

~1.5MB
~38K SLoC