快速入门
在这里,会先介绍一下Diesel的一些简单应用,包括项目的初始化和简单的增删改查。这里的示例需要按照一步步操作,因为之后的操作依赖之前的操作。
这里的示例是使用的是SQLite数据库。在开始操作之前,需要安装了SQLite。但Diesel还支持其他的数据库,更多的请查看Diesel官网。
关于
Rust版本: 要使用Diesel,Rust版本最少要是1.31。同时强烈建议通过rustup update stable使用Rust的最新版本。
初始化项目
首先要做的是初始化一个示例项目,可以使用IDE工具或命令行的方法来初始化项目。在这里,使用命令行初始化项目。
cargo new --lib diesel_democd diesel_demo
接下来,需要添加Diesel依赖。还要使用一个叫.env的工具来为我们管理环境变量。具体的依赖如下:
[dependencies]diesel = { version = "1.4.4", features = ["sqlite"] }dotenv = "0.15.0"
安装Diesel命令行工具
Diesel提供了一个单独的命令行工具。这个命令行工具可以提供很多的方便。为此,可以把这个工具添加到系统当中。
cargo install diesel_cli
需要注意的是,在安装
Diesel的命令行工具的时候,可能会遇到下面类似的错误:
note: ld: library not found for -lmysqlclientclang: error: linker failed with exit code 1 (use -v to see invocation)
这表明在安装
diesel命令行工具的时候,没有安装必要的客户端库(在例子中是mysqlclient)。 默认情况下:diesel命令行工具依赖下面的客户端库:
- PostgreSQL-libpq
- Mysql-libmysqlclient
- SQlite-libsqlite3
当然,在默认情况下,并不需要安装所有的依赖库。可以通过指定的参数来只安装需要的依赖库。如下:
cargo install diesel_cli --no-default-features --features sqlite
为项目设置Diesel
要使用diesel,需要为项目指定数据库的连接地址。为了方便和数据库的安全,需要把数据库的地址放在.env的环境变量中,并把.env文件添加到.gitignore文件中。可以通过下面的方式把数据库地址添加到.env文件中:
echo DATABASE_URL=db.sqlite > .envecho .env >> .gitignore
设置好了数据库地址之后,就可以使用diesel命令行来初始化数据库了。
diesel setup
在运行完了diesel setup后,diesel会创建一个数据库(如果这个数据库不存在),并且创建了一个空的迁移目录,在之后可以通过迁移目录来管理数据库模型。
此外,在运行完
diesel setup后,在项目目录中还生成了一个只含有file字段的diesel.toml文件。
接下来,可以通过diesel命令行以迁移目录的行事来管理项目。先让我们使用命令创建一个迁移目录:
diesel migration generate create_posts
在运行完这个命令之后,diesel命令行工具为我们创建了2个空的文件。可能会看到下面这样的输出:
Creating migrations/2022-05-05-071856_create_posts/up.sqlCreating migrations/2022-05-05-071856_create_posts/down.sql
迁移可以使我们随着时间的迁移来探索数据库。每一个迁移目录都包含一个up.sql文件和一个down.sql文件。
下面让我们来编辑一下up.sql和down.sql:
create table posts (id integer primary key not null,title text not null,body text not null,published int not null default 0)
drop table posts
使用下面的命令来应用迁移:
diesel migration run
还可以使用下面的命令来确定down.sql来确保的正确性:
diesel migration redo
Diesel CLI
Diesel CLI 是Diesel为我们提供的一个管理数据库模型的工具。在管理数据库模型中,Diesel CLI主要扮演了两种角色:数据库迁移和创建数据库模型对应的Rust文件。
我们可以通过设置一个文件来定制Diesel CLI的具体行为。这个文件一般是在Cargo.toml文件同目录下的叫diesel.toml的文件。但我们也可以通过DIESEL_CONFIG_FILE环境变量来指定不同的文件,或者是在命令行中使用--config-file的参数来指定一个其他的配置文件。此外,我们可以通过diesel setup来自动生成一个简单的默认配置文件。
从Diesel 1.3开始,diesel.toml文件包含了一个[print_schema]章节,这个章节中的所有字段都是可选的。
file字段
file字段指定了diesel把数据库模型对应的rust文件放置在项目中的什么位置。如果指定了这个字段的话,当我们运行修改数据库模型的命令(如diesel migration run)的时候,diesel会自动运行diesel print-schema命令,并且把结果输出到指定的文件中。
这意味着可以放心大胆的修改数据库模型还不用担心运行命令的时候rust对应的文件不会得到更新。强烈推荐使用此字段,以便可以使得数据库模型和对应的rust代码始终保持同步。通常情况下,这个字段的值设置为src/schema.rs。
和其他的字段的不同之处在于,这个字段并不会改变diesel print-schema的行为。无论是否设置了这个字段值,print-schema都会把数据库模型输出到标准输出中。
with_docs字段
当我们把with_docs字段设置为true的时候,diesel print-schema会把数据库模型中的表和列中的注释输出。效果和在命令行中使用--with-docs参数一样。
filter字段
filter字段可以设置我们在print-schema命令中输出哪些表。这个字段提供了两种指定表的形式:包含指定的表和排除指定的表。分别使用only_tables和except_tables来指定,两个都是接收数组为参数。示例如下:
[print_schema]# 通过`only_tables` 可指定在输出中只包含 users 和 posts 表filter = { only_talbes = ["users", "posts"] }# 通过`except_tables` 可以指定在输出中排除 comments 表,其他的表都会包含在输出中filter = { except_tables = ["comments"] }
schema字段
schema字段指定了在diesel查找数据表的时候的目标数据库。效果和在命令行中指定--schema一样。这个字段目前只对PostgreSQL数据库有效。如果没有指定这个字段,默认的目标数据库是public。
import_types字段
import_types字段指定了在table!宏中引入的类型声明。效果和在命令行中指定--import-types是一样的。当没有指定该字段的时候,默认引入的是diesel::sql_types声明。指定的字段如下:
[print_schema]# 添加 `diesel_full_text_search` 声明import_types = ["diesel::sql_types::*", "diesel_full_text_search::types::*"]
patch_file字段
常见问题
使用SQlite数据库中的问题
the trait SupportsReturningClause is not implemented for Sqlite
当我们使用类似于下面的代码的时候,就会出现上面的错误:
table! {posts (id) {id -> Integer,title -> Text,body -> Text,published -> Integer}}#[derive(Queryable)]struct Post {id: i32,title: String,body: String,published: i32,}#[derive(Insertable)]#[table_name = "posts"]struct NewPost<'a> {title: &'a str,body: &'a str,}pub fn create_post<'a>(conn: &SqliteConnection,title: &'a str, body: &'a str)-> Post {let new_post = NewPost { title, body };diesel::insert_into().values(&new_post).get_result(conn).expect("Error saving new post")}
这里的报错是因为`sqlite`没有实现在插入完成后返回插入内容的功能。所以,我们可以修改`create_post`方法,让这个方法返回修改的条数。修改后的`create_post`方法如下:
pub fn create_post<'a>(conn: &SqliteConnection, title:&'a str, body:&'a str) -> usize {let new_post = NewPost { title, body };diesel::insert_into().values(&new_post).execute(conn).expect("Error saving new post")}
required because of the requirements on the impl of Query for Paginated<table>
当我们自定义分页查询的时候,可能出现上面的错误。在这个时候,我们只要修改一个调用方式即可(在调用paginate前调用as_query())。
之前的调用方式:
let result = posts.paginate(1).per_page(2).load_and_count_pages::<Post>(&connection).expect("Page Error");
修正后的调用方式:
let result = posts.as_query().paginate(1).per_page(2).load_and_count_pages::<Post>(&connection).expect("Page Error");
使用PostgreSQL数据库的问题
创建插入时间和更新时间字段
要在postgreSQL中使用创建时间和更新时间,可以像下面这样设置sql语句。
create table users(id serial primary key not null ,name varchar not null ,hair_color varchar,create_at timestamp not null default current_timestamp,updated_at timestamp not null default current_timestamp,able bool not null default 'f');select diesel_manage_updated_at('users');
通用问题
chrono::NaiveDateTime: diesel::Expressionis not satisfied
当我们定义一个modle的时候,可能会用到chrono的naiveDateTime字段,这个时候,如果我们使用了#[derive(Serialize)这样的标记,在编译的时候就可能出现上面的错误提示。比如下面的定义:
use chrono::NaiveDateTime;use serde::Serialize;#[derive(Query,Serialize)]pub struct User {pub id: u64,pub name: String,pub age: u8,pub create_at: NaiveDateTime,pub updated_at: NaiveDateTime,}
在这种情况下出现了错误的原因是,当引入`chrono`包的时候,没有引入`serde`特性导致的。只要我们在`Cargo.toml`中引入`chrono`的时候,添加`serde`特性就行。如下:
chrono = { version="0.4.19", features=["serde"] }
