Table

Derive Macro Table 

Source
#[derive(Table)]
{
    // Attributes available to this derive:
    #[table]
}
Expand description

Implements the Table trait for the some struct, and creates a table module for it containing some useful items which allow performing operations on this table.

§Example

#[derive(Table)]
#[table(unique(first_name, last_name))]
struct Person {
    id: i32,
    first_name: String,
    last_name: String,
    age: i32,

    #[table(foreign_key(School))]
    school_id: i32,
}

#[derive(Table)]
struct School {
    id: i32,
    name: String,
}

The Table macro, besides implementing the Table trait for the provided struct, will also create a module which has the same name as the struct, but converted to snake_case. This module will contain useful items which allow peforing operations on the table.

For example, for the Person struct, a module named person will be created, containing the following items:

  • A struct called new (person::new), which contains all fields of a person other than its id. The new struct implements the Insertable trait which allows inserting it to the database. The fields of this struct use the [std::borrow::Borrow] trait to allow for providing things that the actual type can be borrowed us. For example when creating a school using school::new, we can provide a value of type &str for the name field.

  • A struct called new_with_id, same as the new struct but allows specifying a value for the id field.

  • An empty struct called table (person::table), which implements the TableMarker trait. This struct allows you to perform operations on the table like create, drop, delete, find, inner_join.

  • An empty struct called all (person::all) which implements the SelectedValues trait and allows selecting all fields of this table in functions which require selecting custom values.

  • An empty struct for each column in the table. For example in the above example, the created structs will be person::id, person::first_name, person::last_name, person::age and person::school_id. Each of these structs implement the SqlExpression trait.

  • A module named unique_constraints containing marker structs for each unique constraint on the table. For example in the above example, the created structs will be person::unique_constraints::id and person::unique_constraints::first_name_last_name.

§Foreign Keys

Foreign keys can be implemented as shown in the example above using the #[table(foreign_key(...))] attribute, and specifying the table struct’s name.

Foreign keys allow you to perform joins on the tables, and we can then perform select queries on the joined tables, for example, for the above snippet we can do the following:

let _ = person::table.inner_join(school::table)
    .find()
    .filter(school::name.equals("Stanford"))
    .load_all::<Person>(...)
    .await?;

Foreign keys can also be optional, for example:

#[derive(Table)]
pub struct MaybeStudent {
    id: i32,

    #[table(foreign_key(School))]
    school_id: Option<i32>,
}

The inner join of the table MaybeStudent with the table School will then only return the students who’s school_id is not None.

A single table can also have multiple foreign keys to the same table, for example:

#[derive(Table)]
pub struct Student {
    id: i32,

    #[table(foreign_key(School))]
    elementary_school: i32,

    #[table(foreign_key(School))]
    university: i32,
}

If you then want to perform an inner join from Student to School, you will have to use the inner_join_on_column function instead of just using the inner_join function, because you must explicitly tell the orm which column you want to perform the join on, for example:

let _ = student::table.inner_join_on_column(student::university, school::table)
    .find()
    .filter(school::name.equals("Stanford"))
    .load_all::<Student>(...)
    .await?;

Please note that the type of the foreign key field must match the type of the referenced table’s id field.

§Unique Constraints

Unique constraints can be implemented as shown in the example above using the #[table(unique(...))] attribute, and specifying the fields on which the unique constraint should enforce uniqueness.

If you want to add a unique constraint on a single field, you can also add a #[table(unique)] attribute on one of the table struct’s fields, for example: ```rust #[derive(Table)] pub struct Person { #[table(unique)] full_name: String, }

 
You can create multiple unique constraints on a single table by adding
multiple `#[table(unique(...))]` attributes to the table struct.

A unique constraint for the id field is automatically created.

Unique constraints allow you to use `ON CONFLICT` clauses, and to perform
upserts, for example, for the above snippet, we can do the following:
```rust
let upserted_person = person::new {
    first_name: "James",
    last_name: "Brown",
    age: &44,
    school_id: &2,
}
.insert()
.on_conflict(person::unique_constraints::first_name_last_name)
.do_update(update_set!(person::school_id = 2))
.returning(person::all)
.load_one::<Person>(...)
.await?;