实施 Diesel 的 Insertable

implement Diesel's Insertable

我正在构建一个由 Rust 的 Diesel ORM. I would like the URL of a post to include the "slug" of its title. So, posts should be queryable by slug. Therefore I wish to generate the slug from the title using the slugify crate 支持的(gasp)博客,然后将 slug 存储在数据库中 posts table 的相应列中。

因为 posts 也将有一个由数据库生成的数字 ID,我希望将传入的 posts 解析为另一个结构,NewPost。然后 NewPost 应该实现 Diesel 的 Insertable,以便在数据库中记录一个新的 post 足以调用生成的 insert_into 方法。但是,简单地导出Insertable是行不通的,因为需要先生成slug属性的值。

一个选择是引入中间结构 SluggedNewPost,并为其实现 From<NewPost>Insertable 特征:

struct NewPost<'a> {
    title: &'a str,
    content: &'a str,
}

#[derive(Insertable)]
#[table_name="posts"]
struct SluggedNewPost<'a> {
    title: &'a str,
    content: &'a str,
    slug: String,
}

impl <'a> From<NewPost<'a>> for SluggedNewPost<'a> {
    fn from(newpost: NewPost<'a> ) -> Self {
        SluggedNewPost {title: &'a newpost.title,
                        content: newpost.content,
                        slug: slugify(newpost.title)}
    }
}

这适用于我的有限目的。但是直接在NewPost上实现Insertable方法似乎更优雅。我尝试遵循 的建议,但失败了,因为我不理解宏扩展生成的代码(例如,取消引用 values 中的 id 条目的结果是什么元组?)。

尝试手动实施 Insertable 是完全错误的方法吗?或者在这样做时我是否很容易错过一些东西?看来这种东西在经济上应该是可行的。

这里最好的方法可能是不要有明显的 SluggedNewPost。 Diesels #[derive(Insertable)] 旨在用于您已经拥有现有结构的情况,这样您就可以将 derive 放在那里并且一切正常。对于一些额外计算的情况,比如创建密码哈希或计算你的 slug,更直接的基于元组的插入变体是首选。您甚至可以混合使用这两种变体,在这种情况下这似乎是个好主意。所以你的结果代码看起来有点像


#[derive(Insertable)]
#[table_name = "posts"]
struct NewPost<'a> {
    title: &'a str,
    content: &'a str,
}

fn insert_with_slug(new_post: NewPost, conn: &PgConnection) -> QueryResult<()> {
    diesel::insert_into(posts::table)
        .values((new_post, posts::slug.eq(slugify(new_post.title))
        .execute(conn)?;
    Ok(())
}