Skip to content

Improve model guide#515

Open
ElijahAhianyo wants to merge 13 commits into
masterfrom
elijah/model-docs-improve
Open

Improve model guide#515
ElijahAhianyo wants to merge 13 commits into
masterfrom
elijah/model-docs-improve

Conversation

@ElijahAhianyo
Copy link
Copy Markdown
Contributor

@ElijahAhianyo ElijahAhianyo commented Mar 20, 2026

Restructure the Docs, starting with the model docs. I've improved the docs to mention a large number of features/functionalities we support. I've also split the docs to span across multiple pages in a subcategory, making it easier and nicer to follow. Currently, we have 2 pages (overview and queries) for Models. The overview section is a bit nuanced as it makes mention of the CotORM, setting up models, and configuring them.

There's also a section of Relationships which I believe deserves its own page once we have the M2M feature implemented(or we can still put this in its own page and show how to do this using an intermediate through Model).

I would like to add another page for migrations, however, I'm keeping this PR ready at all times for review. Subsequent additions may or may not be included to this PR depending on when this gets merged

Related issue or discussion

Description

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / cleanup
  • Performance improvement
  • Other (describe above)

Checklist

  • I've read the contributing guide
  • Tests pass locally (just test-all)
  • Code passes clippy (just clippy)
  • Code is properly formatted (cargo fmt)
  • New tests added (regression test for bugs, coverage for new features)
  • Documentation (both code and site) updated (if applicable)

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 20, 2026

🐰 Bencher Report

Branchelijah/model-docs-improve
Testbedgithub-ubuntu-latest
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
milliseconds (ms)
(Result Δ%)
Upper Boundary
milliseconds (ms)
(Limit %)
empty_router/empty_router📈 view plot
🚷 view threshold
6.67 ms
(+9.13%)Baseline: 6.11 ms
7.65 ms
(87.24%)
json_api/json_api📈 view plot
🚷 view threshold
1.20 ms
(+11.55%)Baseline: 1.07 ms
1.31 ms
(91.30%)
nested_routers/nested_routers📈 view plot
🚷 view threshold
1.11 ms
(+12.09%)Baseline: 0.99 ms
1.20 ms
(92.97%)
single_root_route/single_root_route📈 view plot
🚷 view threshold
1.05 ms
(+10.02%)Baseline: 0.95 ms
1.16 ms
(90.63%)
single_root_route_burst/single_root_route_burst📈 view plot
🚷 view threshold
17.38 ms
(-1.76%)Baseline: 17.69 ms
21.19 ms
(82.01%)
🐰 View full continuous benchmarking report in Bencher

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

Flag Coverage Δ
rust 89.99% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions github-actions Bot added the A-docs Area: Documentation label Mar 31, 2026
@ElijahAhianyo
Copy link
Copy Markdown
Contributor Author

ElijahAhianyo commented Mar 31, 2026

depends on cot-rs/cot-site#82 and cot-rs/cot-site#84

@ElijahAhianyo
Copy link
Copy Markdown
Contributor Author

Screenshot 2026-04-02 at 3 51 59 PM


```

## Summary
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some pages have a Summary section while others don't. I'm not too sure which direction we'd want to go, but for the sake of consistency, do we require every page to have a Summary section? I, personally, am not a big fan, but I'm also indifferent if we have them or not

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also indifferent if we have them or not, I'd just push on being consistent and having them everywhere or nowhere

@ElijahAhianyo ElijahAhianyo marked this pull request as ready for review April 9, 2026 17:19
@ElijahAhianyo ElijahAhianyo changed the title [WIP]Improve model guide Improve model guide Apr 9, 2026
Copy link
Copy Markdown
Member

@m4tx m4tx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing stuff! There are some issues here and there - please make the changes before we can merge this.

Also, sometimes you put an additional newline after section headers, sometimes you do not - please make this consistent with the rest of the guides.

#[derive(Debug, Clone)]
struct NewType(i32);

impl FromDbValue for NewType {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not exactly happy with how closely tied this is to the specific database backend one can use. I would expect this to be changed in the future when we'll finally land the ORM refactor.

I think it's fine to keep it as-is because there's not much we can do about this for now, but just adding this as a general comment.

struct MyProject;

impl Project for MyProject {
fn config(&self) -> cot::Result<ProjectConfig> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if it's good to put this here. The side effect of such change is that after adding this code, the TOML config will be completely ignored, which might be not what user might want.

I'd suggest either mentioning this (something in the lines of "keep in mind that adding this code will cause the TOML config to be ignored for other config parameters as well"), or remove this section altogether.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, I think I'd prefer explicitly mentioning the side effect than taking out the section altogether

Comment thread docs/databases/overview.md
Comment thread docs/databases/overview.md
Comment thread docs/databases/queries.md
Saving a foreign key field is similar to saving a regular field. There are two variants of foreign key fields provided by cot:

### `ForeignKey::Model`
This is the most common variant which allows you to save a foreign key field with a model instance.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth noting that the instance needs to be saved to the database first.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mm, I'm struggling to make sense of how this fits into the sentence.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this?

Remember, the instance the foreign key points to needs to be saved to the database before the instance containing the foreign key.

Comment thread docs/databases/queries.md Outdated
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@cot::db::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:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd mention that while the trait requires all of that, we've created #[model] macro that does most of that for you. Otherwise we mention that you have to do that and then don't do that in the example


There's some very useful stuff going on here, so let's break it down:

* The [`#[model]`](attr@cot::db::model) attribute is used to mark the structure as a model. This is required for the Cot ORM to recognize it as such.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sth like this

Suggested change
* The [`#[model]`](attr@cot::db::model) attribute is used to mark the structure as a model. This is required for the Cot ORM to recognize it as such.
* The [`#[model]`](attr@cot::db::model) attribute macro is used to automatically implement the [`Model`](trait@cot::db::Model) trait for the structure. This is required for the Cot ORM to recognize it as a database model.

```

### `unique`
This is used to mark a field as unique, which means that each value in this field must be unique across all rows in the table. For more information see the [model field reference](https://docs.rs/cot_macros/0.5.0/cot_macros/attr.model.html).
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the link link to the latest version?

```

## Field Types
To use a type in a model, they **must** implement the [`ToDbValue`](trait@cot::db::ToDbValue) and [`FromDbValue`](trait@cot::db::FromDbValue) traits. The [`ToDbValue`](trait@cot::db::ToDbValue) trait tells Cot how to serialize the field value into a format that can be stored in the database (e.g. a string, a number, a boolean, etc.) while the [`FromDbValue`](trait@cot::db::FromDbValue) trait tells Cot how to deserialize the field value from the database format back into the Rust type.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
To use a type in a model, they **must** implement the [`ToDbValue`](trait@cot::db::ToDbValue) and [`FromDbValue`](trait@cot::db::FromDbValue) traits. The [`ToDbValue`](trait@cot::db::ToDbValue) trait tells Cot how to serialize the field value into a format that can be stored in the database (e.g. a string, a number, a boolean, etc.) while the [`FromDbValue`](trait@cot::db::FromDbValue) trait tells Cot how to deserialize the field value from the database format back into the Rust type.
To use a type in a model, it **must** implement the [`ToDbValue`](trait@cot::db::ToDbValue) and [`FromDbValue`](trait@cot::db::FromDbValue) traits. The [`ToDbValue`](trait@cot::db::ToDbValue) trait tells Cot how to serialize the field value into a format that can be stored in the database (e.g. a string, a number, a boolean, etc.) while the [`FromDbValue`](trait@cot::db::FromDbValue) trait tells Cot how to deserialize the field value from the database format back into the Rust type.

Comment on lines +182 to +185
As an alternative to setting the database configuration in the `TOML` file, you can also set it programmatically in the [`config`](trait@cot::project::Project#method.config) method of your project.
Note that when you do this, the config values from the `TOML` file will be entirely ignored.

Here's an example of how you can set the database configuration programmatically:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
As an alternative to setting the database configuration in the `TOML` file, you can also set it programmatically in the [`config`](trait@cot::project::Project#method.config) method of your project.
Note that when you do this, the config values from the `TOML` file will be entirely ignored.
Here's an example of how you can set the database configuration programmatically:
As an alternative to setting the database configuration in the `TOML` file, you can also set it programmatically in the [`config`](trait@cot::project::Project#method.config) method of your project.
Note that when you do this, the config values from the `TOML` file will be entirely ignored. Here's an example of how you can do that:

Comment thread docs/databases/queries.md
Saving a foreign key field is similar to saving a regular field. There are two variants of foreign key fields provided by cot:

### `ForeignKey::Model`
This is the most common variant which allows you to save a foreign key field with a model instance.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like this?

Remember, the instance the foreign key points to needs to be saved to the database before the instance containing the foreign key.

Comment thread docs/databases/queries.md
Comment on lines +228 to +229
results of a query.The [`Query`](struct@cot::db::query::Query) object can be accessed by calling the [`objects`](trait@cot::db::Model#method.objects) method on the model. The example below shows how to retrieve a
Customer instance with the primary key of `5`.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
results of a query.The [`Query`](struct@cot::db::query::Query) object can be accessed by calling the [`objects`](trait@cot::db::Model#method.objects) method on the model. The example below shows how to retrieve a
Customer instance with the primary key of `5`.
results of a query. The [`Query`](struct@cot::db::query::Query) object can be accessed by calling the [`objects`](trait@cot::db::Model#method.objects) method on the model. The example below shows how to retrieve a Customer instance with the primary key of `5`.

Comment thread docs/databases/queries.md
Comment on lines +241 to +242
The [`filter`](struct@cot::db::query::Query#method.filter) method takes a [`filter expression`](enum@cot::db::query::Expr). In the example above, the expression, `Expr::eq(Expr::field("id"), Expr::value("5"))`, is
evaluated as `id = 5`.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
The [`filter`](struct@cot::db::query::Query#method.filter) method takes a [`filter expression`](enum@cot::db::query::Expr). In the example above, the expression, `Expr::eq(Expr::field("id"), Expr::value("5"))`, is
evaluated as `id = 5`.
The [`filter`](struct@cot::db::query::Query#method.filter) method takes a [`filter expression`](enum@cot::db::query::Expr). In the example above, the expression, `Expr::eq(Expr::field("id"), Expr::value("5"))`, is evaluated as `id = 5`.

Comment thread docs/databases/queries.md
Comment on lines +244 to +245
Cot also provides the [`query!`](macro@cot::db::query) macro which provides a convenient way to write queries in a declarative style without having to manually construct [`Expr`](enum@cot::db::query::Expr) expressions.
The example above can be rewritten as follows:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
Cot also provides the [`query!`](macro@cot::db::query) macro which provides a convenient way to write queries in a declarative style without having to manually construct [`Expr`](enum@cot::db::query::Expr) expressions.
The example above can be rewritten as follows:
Cot also provides the [`query!`](macro@cot::db::query) macro which provides a convenient way to write queries in a declarative style without having to manually construct [`Expr`](enum@cot::db::query::Expr) expressions. The example above can be rewritten as follows:

Comment thread docs/databases/queries.md

Note that this will fail if the primary key of the referenced model does not exist.

## Retrieving objects
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd start with the query!() macro to show how easy it is, and then show the manual way it's implemented inside it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-docs Area: Documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants