pub struct Models { /* private fields */ }Expand description
A collection of Model used by the Models to
define models.
This collection allows you to manage multiple models efficiently, facilitating the process of defining and manipulating them within your application.
§Note
Usually, there is little point in creating models at runtime. In some cases, it is necessary to define them with a 'static lifetime, for example, to address compatibility issues with certain asynchronous libraries such as Axum.
There are multiple ways to achieve this, including the once_cell::sync::Lazy crate,
or the LazyLock from the standard library, which is available when the relevant Rust feature is enabled.
§Example using once_cell::sync::Lazy
use native_db::*;
use once_cell::sync::Lazy;
// The lifetime of the models needs to be longer or equal to the lifetime of the database.
// In many cases, it is simpler to use a static variable but it is not mandatory.
static MODELS: Lazy<Models> = Lazy::new(|| {
let mut models = Models::new();
// It's a good practice to define the models by specifying the version
models.define::<data::v1::Person>().unwrap();
models
});
fn main() -> Result<(), db_type::Error> {
// Initialize the database with the models
let db = Builder::new().create_in_memory(&MODELS)?;
Ok(())
}Implementations§
Source§impl Models
impl Models
Sourcepub fn define<T: ToInput>(&mut self) -> Result<()>
pub fn define<T: ToInput>(&mut self) -> Result<()>
Defines a table using the given model.
Native DB depends on native_model to define the model.
By default, native_model uses serde to serialize and deserialize the data, but
you can use any other serialization library. See the documentation of native_model for more information.
In the examples below, we import serde and use the Serialize and Deserialize traits.
§Primary Key
The primary key is mandatory, and you must:
- Define it.
- Define only one.
If the primary key is not defined, the compiler will return an error: Primary key is not set.
There are two ways to define a primary key:
-
On a Field:
- Use the
#[primary_key]attribute on the field that will serve as the primary key. - The type of the field will be used as the primary key type.
- Use the
-
With a Custom Method:
- Use the
#[native_db(primary_key(<method_name> -> <return_type>))]attribute on the type (enum,struct,tuple struct, orunit struct). - Implement a method with the given
<method_name>that returns the primary key of type<return_type>. - Important: You must specify both the method name and the return type using the syntax
primary_key(<method_name> -> <return_type>). The type must be specified because it is used at runtime to check the query types.
- Use the
The primary key is unique, so you can’t have two instances of the model with the same primary key saved in the database.
§Defining a Simple Model with a Primary Key on a Field
use native_db::*;
use native_model::{native_model, Model};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[native_model(id=1, version=1)]
#[native_db]
struct Data {
#[primary_key]
id: u64,
}
fn main() -> Result<(), db_type::Error> {
let mut models = Models::new();
models.define::<Data>()
}In this example, we have:
- One primary key named
idof typeu64, defined directly on the field using the#[primary_key]attribute.
§Defining a Model with a Method as Primary Key
use native_db::*;
use native_model::{native_model, Model};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[native_model(id=1, version=1)]
#[native_db(
primary_key(custom_id -> u32)
)]
struct Data(u64);
impl Data {
fn custom_id(&self) -> u32 {
(self.0 + 1) as u32
}
}In this example, we have:
- One primary key named
custom_idof typeu32, defined using a custom method. The methodcustom_idcomputes and returns the primary key value.
§Secondary Key
The secondary key is flexible, and you can:
- Define it or not.
- Define one or more.
There are two ways to define a secondary key:
-
On a Field:
- Use the
#[secondary_key]attribute on the field that will serve as a secondary key. - The type of the field will be used as the secondary key type.
- Use the
-
With a Custom Method:
- Use the
#[native_db(secondary_key(<method_name> -> <return_type>, <options>))]attribute on the type. - Implement a method with the given
<method_name>that returns the secondary key value of type<return_type>. - Important: You must specify both the method name and the return type using the syntax
secondary_key(<method_name> -> <return_type>, <options>). The type must be specified because it is used at runtime to check the query types.
- Use the
The secondary key can have two options:
§Defining a Model with a Secondary Key on a Field
use native_db::*;
use native_model::{native_model, Model};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[native_model(id=1, version=1)]
#[native_db]
struct Data {
#[primary_key]
id: u64,
#[secondary_key]
name: String,
}In the above example, we have:
- One primary key named
idof typeu64, defined on the field. - One secondary key named
nameof typeString, defined on the field using the#[secondary_key]attribute.
§Defining a Model with an Optional and Unique Secondary Key
use native_db::*;
use native_model::{native_model, Model};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[native_model(id=1, version=1)]
#[native_db]
struct Data {
#[primary_key]
id: u64,
#[secondary_key(unique, optional)]
name: Option<String>,
}In the above example, we have:
-
One primary key named
idof typeu64, defined on the field. -
One secondary key named
nameof typeOption<String>, defined on the field with optionsuniqueandoptional. -
Note: The secondary key can be
unique,optional, or both.
§Unique
This means that each instance of the model must have a unique value for the secondary key.
If the value is not unique, the insert method will return an error.
§Optional
This means that an instance of the model can have a value for the secondary key or not.
When optional is set, the value must be an Option.
If the value is not an Option, the compiler will return
an error: error[E0282]: type annotations needed: cannot infer type.
Under the hood, the secondary key is stored in a separate redb table. So if the secondary key is optional,
the value will be stored in the table only if the value is not None.
§Defining a Model with a Custom Optional Secondary Key
use native_db::*;
use native_model::{native_model, Model};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[native_model(id=1, version=1)]
#[native_db(
secondary_key(custom_name -> Option<String>, optional)
)]
struct Data {
#[primary_key]
id: u64,
#[secondary_key]
name: String,
flag: bool,
}
impl Data {
fn custom_name(&self) -> Option<String> {
if self.flag {
Some(self.name.clone().to_uppercase())
} else {
None
}
}
}In the above example, we have:
- One primary key named
idof typeu64, defined on the field. - One secondary key named
nameof typeString, defined on the field. - One custom secondary key named
custom_nameof typeOption<String>, defined using a custom method with the optionoptional.
The method custom_name returns an Option<String> based on some logic involving the flag field.
§Defining Multiple Models
To define multiple models, you must use different id values for each model. If you use the same id for two models,
the program will panic with the message: The table <table_name> has the same native model version as the table <table_name> and it's not allowed.
Example:
use native_db::*;
use native_model::{native_model, Model};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[native_model(id=1, version=1)]
#[native_db]
struct Animal {
#[primary_key]
name: String,
}
#[derive(Serialize, Deserialize)]
#[native_model(id=2, version=1)]
#[native_db]
struct Vegetable {
#[primary_key]
name: String,
}
fn main() -> Result<(), db_type::Error> {
let mut models = Models::new();
models.define::<Animal>()?;
models.define::<Vegetable>()
}In the above example, we have:
- We have two models,
AnimalandVegetable. - Both have:
- One primary key named
nameof typeString, defined on the field.
- One primary key named
- Each model has a unique
id(id=1forAnimal,id=2forVegetable), which is necessary to avoid conflicts.