Create records
Rwf can create model records in one of two ways:
Model::save
method which is called on an instance of a struct implementing theModel
traitModel::create
method which accepts the column names and their respective values as input
Saving models
Using our User
model from our previous example, we can create a new record by instantiating a new instance of the User
struct and calling save
:
let user = User {
id: None,
email: "admin@example.com".to_string(),
created_at: OffsetDateTime::now_utc(),
};
let user = user
.save()
.fetch(&mut conn)
.await?;
Note
The id
field is set to None
. This ensures that the database
assigns it a value automatically, and that this value is unique.
Calling save
on a model struct with the id
set to None
produces the following query:
Using table defaults
If you don't want to specify some columns when creating records and your database schema has configured defaults, you can use the Model::create
method instead:
Any columns not specified in the INSERT
statement will be automatically filled in with column defaults. For example, the created_at
column
specified in our previous example has a default value NOW()
, the current database time.
Unique constraints
It's very common to place unique constraints on certain columns in a table to avoid duplicate records. For example, the "users"
table
would typically have a unique constraint on the email
column, ensuring that no two users have the same email address.
To handle unique constraints, Rwf can update a record in-place if one exists already matching the constraint:
Optionally create records
If the record matching the INSERT
statement exists already, Rwf supports returning the existing row without performing an update:
This executes up to two queries, starting with a SELECT
to see if a row already exists, and if it doesn't, an INSERT
to create it.
Combining with a unique constraint
In busy web apps which execute thousands of queries per second, it's entirely possible for a record to be created between the time the SELECT
query
returns no rows and an INSERT
query is sent to the database. In this case, a unique constraint violation error will be returned. To avoid this,
it's possible to combine unque_by
with find_or_create_by
executed inside a single transaction:
A transaction is started explicitly:
Afterwards, the ORM attempts to find a record matching the columns
in the INSERT
statement:
If this query returns a row, no more queries are executed. Otherwise,
an INSERT
query with ON CONFLICT
clause is sent to the database:
INSERT INTO "users" ("email") VALUES ($1)
ON CONFLICT ("email") DO UPDATE
SET "email" = EXCLUDED."email"
RETURNING *
Finally, the transaction is committed to the database: