我正在建立一个由Rust的Diesel ORM支持的(令人讨厌的)博客。我希望帖子的网址包含其标题的“ slug”。因此,帖子应该可以被子弹查询。因此,我希望使用slugify板条箱从标题生成条块,然后将条块存储在数据库中posts表的相应列中。
由于帖子还将具有一个由数据库生成的数字ID,因此我希望将传入的帖子解析为另一个结构NewPost
。然后NewPost
应该实现Diesel的Insertable,以便在数据库中记录新帖子,只需调用生成的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)}
}
}
这只能用于我的有限目的。但是,直接在Insertable
上实现NewPost
方法似乎更优雅。我尝试遵循this answer的建议,但失败了,因为我不理解宏扩展生成的代码(例如,取消引用id
中的values
条目的结果是什么元组?)。
尝试手动实现Insertable
是否完全是错误的方法?还是我这样做很容易错过?看来这种事情在经济上应该是可行的。
答案 0 :(得分:2)
这里最好的方法是不要使用不同的SluggedNewPost
。柴油#[derive(Insertable)]
用于在已有结构的情况下使用,因此您可以将derive
放在这里,然后一切正常。对于某些其他计算(例如,创建密码哈希或计算您的子弹)的情况,首选基于直接元组的insert变体。您甚至可以混合使用两种变体,在这种情况下,这似乎是个好主意。因此,您生成的代码可能看起来像
#[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(())
}