如何从辅助方法动态返回 Diesel 等式表达式?
Posted
技术标签:
【中文标题】如何从辅助方法动态返回 Diesel 等式表达式?【英文标题】:How can I dynamically return a Diesel equality expression from a helper method? 【发布时间】:2020-08-19 01:48:18 【问题描述】:我有一个枚举:
enum Role
Administrator,
Sponsor,
Bot,
我的数据库中有一个名为“roles”的表,如下所示:
| user_id | administrator | sponsor | bot |
|---------|---------------|---------|-------|
| 0 | true | false | false |
| 1 | false | false | false |
| 2 | false | true | true |
如何在使用 Diesel 而不是原始 SQL 查询时对 Role
执行匹配并获取数据库中的相应列?
我可以从与角色匹配的方法中返回 Diesel 表达式,但它不适用于我的插入语句:
#[macro_use]
extern crate diesel;
use diesel::
mysql::Mysql, MysqlConnection,
sql_types::Bool, Nullable,
BoxableExpression, Connection, ExpressionMethods, RunQueryDsl,
;
use schema::roles, users;
use models::NewUser;
use std::error::Error;
mod schema
table!
roles (user_id)
id -> Unsigned<Bigint>,
user_id -> Unsigned<Bigint>,
administrator -> Nullable<Bool>,
sponsor -> Nullable<Bool>,
bot -> Nullable<Bool>,
table!
users (id)
id -> Unsigned<Bigint>,
username -> Nullable<Varchar>,
allow_tables_to_appear_in_same_query!(
roles,
users,
);
mod models
use super::schema::users;
#[derive(Insertable, PartialEq, Debug, Default)]
#[table_name = "users"]
pub struct NewUser<'a>
username: &'a str,
impl<'a> NewUser<'a>
pub fn new(username: &'a str) -> Self
Self
username
enum Role
Administrator,
Sponsor,
Bot,
impl From<&Role> for Box<dyn BoxableExpression<roles::table, Mysql, SqlType = Nullable<Bool>>>
fn from(r: &Role) -> Self
match r
Role::Administrator => Box::new(roles::dsl::administrator),
Role::Sponsor => Box::new(roles::dsl::sponsor),
Role::Bot => Box::new(roles::dsl::bot),
fn main() -> Result<(), Box<dyn Error>>
let conn = MysqlConnection::establish("mysql://localhost/stquestion")?;
diesel::replace_into(users::table)
.values(&NewUser::new("test_account"))
.execute(&conn)?;
diesel::replace_into(roles::table)
.values((
roles::dsl::user_id.eq(1),
<&Role as Into<
Box<dyn BoxableExpression<roles::table, Mysql, SqlType = Nullable<Bool>>>,
>>::into(&Role::Administrator)
.eq(true),
))
.execute(&conn)?;
Ok(())
上面的代码给我留下了以下编译错误:
error[E0277]: the trait bound `std::boxed::Box<dyn diesel::expression::BoxableExpression<***_questions::
schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool
>>>: diesel::query_source::Column` is not satisfied
--> src/main.rs:36:17
|
36 | .values((
| _________________^
37 | | roles::dsl::user_id.eq(1),
38 | | <&Role as Into<
39 | | Box<dyn BoxableExpression<roles::table, Mysql, SqlType = Nullable<Bool>>>,
40 | | >>::into(&Role::Administrator)
41 | | .eq(true),
42 | | ))
| |_________^ the trait `diesel::query_source::Column` is not implemented for `std::boxed::Box<dyn diesel::expr
ession::BoxableExpression<***_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType =
diesel::sql_types::Nullable<diesel::sql_types::Bool>>>`
|
= note: required because of the requirements on the impl of `diesel::insertable::Insertable<***_quest
ions::schema::roles::table>` for `diesel::expression::operators::Eq<std::boxed::Box<dyn diesel::expression::Boxabl
eExpression<***_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_ty
pes::Nullable<diesel::sql_types::Bool>>>, diesel::expression::bound::Bound<diesel::sql_types::Nullable<diesel::sql
_types::Bool>, bool>>`
= note: required because of the requirements on the impl of `diesel::insertable::Insertable<***_quest
ions::schema::roles::table>` for `(diesel::expression::operators::Eq<***_questions::schema::roles::colum
ns::user_id, diesel::expression::bound::Bound<diesel::mysql::types::Unsigned<diesel::sql_types::BigInt>, u64>>, di
esel::expression::operators::Eq<std::boxed::Box<dyn diesel::expression::BoxableExpression<***_questions:
:schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Boo
l>>>, diesel::expression::bound::Bound<diesel::sql_types::Nullable<diesel::sql_types::Bool>, bool>>)`
>error[E0277]: the trait bound `std::boxed::Box<dyn diesel::expression::BoxableExpression<***_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool>>>: diesel::query_source::Column` is not satisfied
--> src/main.rs:43:10
|
43 | .execute(&conn)?;
| ^^^^^^^ the trait `diesel::query_source::Column` is not implemented for `std::boxed::Box<dyn diesel::expression::BoxableExpression<***_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool>>>`
|
= note: required because of the requirements on the impl of `diesel::insertable::InsertValues<***_questions::schema::roles::table, diesel::mysql::backend::Mysql>` for `diesel::insertable::ColumnInsertValue<std::boxed::Box<dyn diesel::expression::BoxableExpression<***_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool>>>, diesel::expression::bound::Bound<diesel::sql_types::Nullable<diesel::sql_types::Bool>, bool>>`
= note: required because of the requirements on the impl of `diesel::insertable::InsertValues<***_questions::schema::roles::table, diesel::mysql::backend::Mysql>` for `(diesel::insertable::ColumnInsertValue<***_questions::schema::roles::columns::user_id, diesel::expression::bound::Bound<diesel::mysql::types::Unsigned<diesel::sql_types::BigInt>, u64>>, diesel::insertable::ColumnInsertValue<std::boxed::Box<dyn diesel::expression::BoxableExpression<***_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool>>>, diesel::expression::bound::Bound<diesel::sql_types::Nullable<diesel::sql_types::Bool>, bool>>)`
= note: required because of the requirements on the impl of `diesel::query_builder::QueryFragment<diesel::mysql::backend::Mysql>` for `diesel::query_builder::insert_statement::ValuesClause<(diesel::insertable::ColumnInsertValue<***_questions::schema::roles::columns::user_id, diesel::expression::bound::Bound<diesel::mysql::types::Unsigned<diesel::sql_types::BigInt>, u64>>, diesel::insertable::ColumnInsertValue<std::boxed::Box<dyn diesel::expression::BoxableExpression<***_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool>>>, diesel::expression::bound::Bound<diesel::sql_types::Nullable<diesel::sql_types::Bool>, bool>>), ***_questions::schema::roles::table>`
= note: required because of the requirements on the impl of `diesel::query_builder::QueryFragment<diesel::mysql::backend::Mysql>` for `diesel::query_builder::insert_statement::InsertStatement<***_questions::schema::roles::table, diesel::query_builder::insert_statement::ValuesClause<(diesel::insertable::ColumnInsertValue<***_questions::schema::roles::columns::user_id, diesel::expression::bound::Bound<diesel::mysql::types::Unsigned<diesel::sql_types::BigInt>, u64>>, diesel::insertable::ColumnInsertValue<std::boxed::Box<dyn diesel::expression::BoxableExpression<***_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool>>>, diesel::expression::bound::Bound<diesel::sql_types::Nullable<diesel::sql_types::Bool>, bool>>), ***_questions::schema::roles::table>, diesel::query_builder::insert_statement::Replace>`
= note: required because of the requirements on the impl of `diesel::query_dsl::load_dsl::ExecuteDsl<_, diesel::mysql::backend::Mysql>` for `diesel::query_builder::insert_statement::InsertStatement<***_questions::schema::roles::table, diesel::query_builder::insert_statement::ValuesClause<(diesel::insertable::ColumnInsertValue<***_questions::schema::roles::columns::user_id, diesel::expression::bound::Bound<diesel::mysql::types::Unsigned<diesel::sql_types::BigInt>, u64>>, diesel::insertable::ColumnInsertValue<std::boxed::Box<dyn diesel::expression::BoxableExpression<***_questions::schema::roles::table, diesel::mysql::backend::Mysql, SqlType = diesel::sql_types::Nullable<diesel::sql_types::Bool>>>, diesel::expression::bound::Bound<diesel::sql_types::Nullable<diesel::sql_types::Bool>, bool>>), ***_questions::schema::roles::table>, diesel::query_builder::insert_statement::Replace>`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.
error: could not compile `***-questions`.
To learn more, run the command again with --verbose.
我为这个问题创建了一个可重现的示例here。
【问题讨论】:
很难回答您的问题,因为它不包含minimal reproducible example。我们无法分辨代码中存在哪些 crate(及其版本)、类型、特征、字段等。如果您尝试在一个全新的 Cargo 项目中重现您的错误,我们会更轻松地为您提供帮助,然后 edit 您的问题将包含附加信息。您可以使用 Rust-specific 和 Diesel-specific MRE 提示来减少您在此处发布的原始代码。谢谢! 请edit 您的问题并粘贴您收到的确切和全部错误 - 这将帮助我们了解问题所在,以便我们提供最佳帮助。有时试图解释错误消息很棘手,实际上错误消息的不同部分很重要。请使用直接运行编译器的消息,而不是 IDE 生成的消息,它可能会尝试为您解释错误。 更新:我已经附加了一个带有Cargo.toml
文件的 GitHub 存储库链接,并在我的原始问题中添加了一个最小的可重现示例。感谢您的帮助。
【参考方案1】:
在我看来,使用BoxableExpression
是错误的方式。为您的枚举手动实现 Insertable<roles::table>
然后直接从那里插入值非常容易。
使用这个Insertable
impl
impl Insertable<roles::table> for Role
type Values = <(
diesel::dsl::Eq<roles::administrator, bool>,
diesel::dsl::Eq<roles::sponsor, bool>,
diesel::dsl::Eq<roles::bot, bool>,
) as Insertable<roles::table>>::Values;
fn values(self) -> Self::Values
use crate::schema::roles;
match self
Role::Administrator => (
roles::administrator.eq(true),
roles::sponsor.eq(false),
roles::bot.eq(false),
),
Role::Sponsor => (
roles::administrator.eq(false),
roles::sponsor.eq(true),
roles::bot.eq(true),
),
Role::Bot => (
roles::administrator.eq(false),
roles::sponsor.eq(false),
roles::bot.eq(true),
),
.values()
可以通过以下方式编写插入查询:
diesel::replace_into(roles::table)
.values((roles::dsl::user_id.eq(1), Role::Administrator))
.execute(&conn)?;
【讨论】:
但是,如果我只想更新一个字段,而其他字段保持不变,这仍然有效吗? 在这种情况下,Insertable
的 impl 需要稍作更改,否则可以。 (基本上您需要将values
函数返回的元组更改为(Option<Eq<…>>, Option<Eq<…>>, Option<Eq<…>>)
并返回None
值而不是.eq(false)
。)以上是关于如何从辅助方法动态返回 Diesel 等式表达式?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Rust Diesel 中使用时间戳和间隔进行算术运算
如何从仅返回 1 或 0 条记录的 Diesel 查询中获取 Option<T> 而不是 Option<Vec<T>>?