Guide chapters
Database models
Cot comes with its own ORM (Object-Relational Mapping) system, which is a layer of abstraction that allows you to interact with your database using objects instead of raw SQL queries. This makes it easier to work with your database and allows you to write more maintainable code. It abstracts over the specific database engine that you are using, so you can switch between different databases without changing your code. The Cot ORM is also capable of automatically creating migrations for you, so you can easily update your database schema as your application evolves, just by modifying the corresponding Rust structures.
Defining models
To define a model in Cot, you need to create a new Rust structure that implements the Model
trait. This trait requires you to define the name of the table that the model corresponds to, as well as the fields that the table should have. Here’s an example of a simple model that represents a link in a link shortener service:
use ;
pub
There’s some very useful stuff going on here, so let’s break it down:
- The
#[model]
attribute is used to mark the structure as a model. This is required for the Cot ORM to recognize it as such. - The
id
field is a typical database primary key, which means that it uniquely identifies each row in the table. It’s of typei64
, which is a 64-bit signed integer.Auto
wrapper is used to automatically generate a new value for this field when a new row is inserted into the table (AUTOINCREMENT
orSERIAL
value in the database nomenclature). - The
slug
field is marked asunique
, which means that each value in this field must be unique across all rows in the table. It’s of typeLimitedString<32>
, which is a string with a maximum length of32
characters. This is a custom type provided by Cot that ensures that the string is not longer than the specified length at the time of constructing an instance of the structure.
After putting this structure in your project, you can use it to interact with the database. Before you do that though, it’s necessary to create the table in the database that corresponds to this model. Cot CLI has got you covered and can automatically create migrations for you – just run the following command:
This will create a new file in your migrations
directory in the crate’s src directory. We will come back to the contents of this file later in this guide, but for now, let’s focus on how to use the model to interact with the database.
Common operations
Saving models
In order to write a model instance to the database, you can use the save
method. Note that you need to have an instance of the Database
structure to do this – typically you can get it from the request object in your view. Here’s an example of how you can save a new link to the database inside a view:
use ;
async
Updating models
Updating a model is similar to saving a new one, but you need to have an existing instance of the model that you want to update, or another instance with the same primary key. Here’s an example of how you can update an existing link in the database:
= "https://example.org";
?;
Note that .save()
is a convenient method that can be used for both creating new rows and updating existing ones. If the primary key of the model is set to Auto
, the method will always create a new row in the database. If the primary key is set to a specific value, the method will update the row with that primary key, or create a new one if it doesn’t exist.
If you specifically want to update a row in the database for given primary key, you can use the update
method:
= "https://example.org";
?;
Similarly, if you want to insert a new row in the database and cause an error if a row with the same primary key already exists, you can use the insert
method:
let mut link = Link ;
?;
Retrieving models
The basis for retrieving models from the database is the Query
structure. It contains information about which model you want to retrieve and allows you to filter, sort, and limit the results.
The easiest way to work with the Query
structure is the query!
macro, which allows you to write complicated queries in readable way using Rusty syntax. For example, to retrieve the link which has slug “cot” from the database, you can write:
use ;
let link = query!
?;
As you can see, the query!
macro takes the model type as the first argument, followed by the filter expression. The filter expression supports many of the common comparison operators, such as ==
, !=
, >
, <
, >=
, and <=
. You can also use logical operators like &&
and ||
to combine multiple conditions. The $
sign is used to access the fields of the model in the filter expression—this is needed so that the macro can differentiate between fields of the model and other variables. What’s nice about the filter expression is that it’s type-checked at compile time, so not only you won’t be able to filter using a non-existent field, but also you won’t be able to compare fields of different types.
Deleting models
To delete a model from the database, you can use the delete
method of the Query
object returned by the query!
macro. Here’s an example of how you can delete a link from the database:
query!?;
Foreign keys
To define a foreign key relationship between two models, you can use the ForeignKey
type. Here’s an example of how you can define a foreign key relationship between a Link
model and some other User
model:
use ;
pub
pub
When you define a foreign key relationship, Cot will automatically create a foreign key constraint in the database. This constraint will ensure that the value in the user_id
field of the Link
model corresponds to a valid primary key in the User
model.
When you retrieve a model that has a foreign key relationship, Cot will not automatically fetch the related model and populate the foreign key field with the corresponding value. Instead, you need to explicitly fetch the related model using the get
method of the ForeignKey
object. Here’s an example of how you can fetch the related user for a link:
let mut link = query!
?
;
let user = ?;
Database Configuration
Configure your database connection in the configuration files inside your config
directory:
[]
# SQLite
= "sqlite://db.sqlite3?mode=rwc"
# Or PostgreSQL
= "postgresql://user:password@localhost/dbname"
# Or MySQL
= "mysql://user:password@localhost/dbname"
Cot tries to be as consistent as possible when it comes to the database engine you are using. This means that you can use SQLite for development and testing, and then switch to PostgreSQL or MySQL for production without changing your code. The only thing you need to do is to change the url
value in the configuration file!
Summary
In this chapter you’ve learned how to define your own models in Cot, how to interact with the database using these models, and how to define foreign key relationships between models. In the next chapter, we’ll try to register these models in the admin panel so that you can manage them through an easy-to-use web interface.