如何访问在 Diesel 中一般访问的表的具体列?

Posted

技术标签:

【中文标题】如何访问在 Diesel 中一般访问的表的具体列?【英文标题】:How to access concrete columns of a table accessed generically in Diesel? 【发布时间】:2021-11-07 19:30:19 【问题描述】:

我很难为这个问题选择一个准确而简洁的标题。

这个问题扩展了@Shepmaster 在这里给出的出色答案:https://***.com/a/47880065/3224771

解决办法是:

use diesel::expression::operators::Desc;
use diesel::helper_types::Limit, Order;
use diesel::query_dsl::methods::LimitDsl, OrderDsl;
use diesel::query_dsl::LoadQuery;

pub fn get_most_recent_entry<'a, Tbl, Expr, Record>(
    conn: &SqliteConnection,
    table: Tbl,
    time: Expr,
) -> Result<i32, String>
where
    Expr: diesel::ExpressionMethods,
    Tbl: OrderDsl<Desc<Expr>>,
    Order<Tbl, Desc<Expr>>: LoadQuery<SqliteConnection, Record> + LimitDsl,
    Limit<Order<Tbl, Desc<Expr>>>: LoadQuery<SqliteConnection, Record>,
    Record: Time,

    table
        .order(time.desc())
        .first(conn)
        .optional()
        .map(|x| x.map_or(0, |x| x.time()))
        .map_err(|e| format!("Error here! :?", e))

在上面的 sn-p 中,TableExpression 作为参数提供。如何进一步抽象此函数,使TableExpression 都不必作为参数传递?

我已经知道如何抽象Table(见下文),但我不知道如何删除Expression 参数,以便该函数可以与包含time 专栏。

use diesel::expression::operators::Desc;
use diesel::helper_types::Limit, Order;
use diesel::query_dsl::methods::LimitDsl, OrderDsl;
use diesel::query_dsl::LoadQuery;
use diesel::SqliteConnection;
use crate::diesel::RunQueryDsl, OptionalExtension;
use diesel::associations::HasTable;

pub trait Time 
    fn time(&self) -> i32;


pub fn get_most_recent_entry<'a, Tbl, Expr, Record>(
    conn: &SqliteConnection,
    time: Expr,
) -> Result<i32, String>
where
    Expr: diesel::ExpressionMethods,
    Tbl: HasTable,
    Tbl::Table: OrderDsl<Desc<Expr>>,
    Order<Tbl::Table, Desc<Expr>>: LoadQuery<SqliteConnection, Record> + LimitDsl,
    Limit<Order<Tbl::Table, Desc<Expr>>>: LoadQuery<SqliteConnection, Record>,
    Record: Time,

    Tbl::table()
        .order(time.desc())
        .first(conn)
        .optional()
        .map(|x| x.map_or(0, |x| x.time()))
        .map_err(|e| format!("Error here! :?", e))

更新

类似这样的:

# Cargo.toml
[dependencies]
diesel =  version = "1.4.5", features = ["sqlite", "extras"] 
#[derive(Queryable)]
pub struct Cat 
    id: u32,
    name: String,
    time: i32 //time record created


impl Time for Cat 
    fn time(&self) -> i32 
        self.time
    


pub fn get_most_recent_entry<'a, Tbl, Record>(
    conn: &SqliteConnection
) -> Result<i32, String>
where 
    // Don't know how to setup trait bounds for the expression
    // to express any table that has the same time column

    Tbl::table()
        .order(Tbl::columns::time.desc()) // Something like this
        .first(conn)
        .optional()
        .map(|x| x.map_or(0, |x| x.time()))
        .map_err(|e| format!("Error here! :?", e))


use crate::schema::cat;
fn main() 
    let conn = pool.get()?;
    get_most_recent_entry::<cat::dsl::cat, _>(&conn)




【问题讨论】:

这个问题真的很难回答。请添加必要的上下文。这至少包括柴油和生锈正在使用的具体版本,对该函数的示例性调用,尤其是关于什么应该被允许作为时间表达和什么不能作为时间表达的更多细节。由于代码是当前编写的,它在那里接受任意表达式,您不能只在函数内部构造而不会失去功能。 @weiznich 已更新,谢谢 【参考方案1】:

不可能以这种通用方式访问列。每列都是它自己的零大小结构,放置在以表命名的模块中。 (有关table! 生成的代码的详细信息,请参阅"Schema in depth" guide)。 Rust 不提供任何选项来通过泛型处理来自同一模块的不同类型。

可以只提供你自己的特性,允许你指定对应的列并为对应的表(或模型,或任何你想要的类型)实现它:

trait MyTimeColumnHelper 
    type TimeColumn: Default;



impl MyTimeColumnHelper for crate::schema::cat::table 
     type TimeColumn = crate::schema::cat::time;

这样你的泛型函数就可以绑定到这个额外的特征:

pub fn get_most_recent_entry<'a, Tbl, Record>(
    conn: &SqliteConnection
) -> Result<i32, String>
where 
    MyTimeColumnHelper::TimeColumn: diesel::ExpressionMethods,
    Tbl: HasTable + MyTimeColumnHelper,
    Tbl::Table: OrderDsl<Desc<MyTimeColumnHelper::TimeColumn>>,
    Order<Tbl::Table, Desc<MyTimeColumnHelper::TimeColumn>>: LoadQuery<SqliteConnection, Record> + LimitDsl,
    Limit<Order<Tbl::Table, Desc<MyTimeColumnHelper::TimeColumn>>>: LoadQuery<SqliteConnection, Record>,
    Record: Time,
    Tbl::Table: OrderDsl<Desc<MyTimeColumnHelper::TimeColumn>>,
    Order<Tbl::Table, Desc<MyTimeColumnHelper::TimeColumn>>: LoadQuery<SqliteConnection, Record> + LimitDsl,
    Limit<Order<Tbl::Table, Desc<MyTimeColumnHelper::TimeColumn>>>: LoadQuery<SqliteConnection, Record>,
    Record: Time,

    Tbl::table()
        .order(MyTimeColumnHelper::TimeColumn::default().desc()) 
        .first(conn)
        .optional()
        .map(|x| x.map_or(0, |x| x.time()))
        .map_err(|e| format!("Error here! :?", e))

【讨论】:

以上是关于如何访问在 Diesel 中一般访问的表的具体列?的主要内容,如果未能解决你的问题,请参考以下文章

通过 psycopg2 访问表的栅格列

如何访问 Acumatica 业务逻辑中的表的扩展

在 hadoop 中保存和访问类似表的数据结构

如何从具有两个外键的表中访问列?

Diesel 可以在运行时更改架构吗?

如何在 Redshift 中找到访问次数最多的表?