Как отформатировать результат SQL, используя дизель? - PullRequest
0 голосов
/ 25 апреля 2020

Я использую Diesel для запроса БД в PostgreSQL, используя JOIN:

let product_id = 1;
sales::table
     .inner_join(product::table)
     .select((
         product::description,
         sales::amount,
         sales::date_sale
     ))
     .filter(sales::product_id.eq(product_id))
     .load(&diesel::PgConnection)

Моя модель:

pub struct Sales {
    pub id: i32,
    pub product_id: Option<i32>,
    pub amount: Option<BigDecimal>,
    pub date_sale: Option<NaiveDateTime>
}

Результат, как и ожидалось, но мне нужно дать формат даты в поле sales::date_sale, что в pgadmin я делаю это с to_char(date_sale, 'dd/mm/YYYY').

Можно ли использовать to_char в Diesel или каким образом я могу изменить данные, которые приносит Diesel ORM мне

Ответы [ 2 ]

0 голосов
/ 27 апреля 2020

В дополнение к ответам, предоставленным harmic, есть еще две возможности для решения этой проблемы.

sql_function!

Дизель обеспечивает простой интерфейс определить узлы запроса ast для sql функций, которые не предоставляются самим дизелем. Пользователям рекомендуется использовать эту функцию для самостоятельного определения отсутствующих функций. (Фактически дизель использует один и тот же метод для определения узлов запросов ast для sql функций, предоставляемых из коробки). Определенный узел запроса может использоваться в любом контексте, где допустимы типы его выражения, поэтому его можно использовать в предложениях select и where. (Это в основном типобезопасная версия вышеупомянутого решения sql)

Для данного вопроса должно работать что-то подобное:

#[derive(Queryable)]
pub struct Sales {
    pub id: i32,
    pub product_id: Option<i32>,
    pub amount: Option<BigDecimal>,
    pub date_sale: Option<String>,
}

sql_function! {
    fn to_char(Nullable<Timestamp>, Text) -> Nullable<Text>;
}

let product_id = 1;
sales::table
     .inner_join(product::table)
     .select((
         product::description,
         sales::amount,
         to_char(sales::date_sale, "dd/mm/YYYY")
     ))
     .filter(sales::product_id.eq(product_id))
     .load(&diesel::PgConnection);

#[derive(Queryable)] + #[diesel(deserialize_as = "Type")]

Дизель Queryable производная обеспечивает способ применения определенных типов манипуляций во время загрузки через пользовательский атрибут. В сочетании с решением chrono, предоставленным harmic, это дает следующий вариант:

#[derive(Queryable)]
pub struct Sales {
    pub id: i32,
    pub product_id: Option<i32>,
    pub amount: Option<BigDecimal>,
    #[diesel(deserialize_as = "MyChronoTypeLoader")]
    pub date_sale: Option<String>
}

struct MyChronoTypeLoader(Option<String>);

impl Into<Option<String>> for MyChronoTypeLoader {
    fn into(self) -> String {
        self.0
    }
}

impl<DB, ST> Queryable<ST, DB> for MyChronoTypeLoader
where
    DB: Backend,
    Option<NaiveDateTime>: Queryable<ST, DB>,
{
    type Row = <Option<NaiveDateTime> as Queryable<ST, DB>>::Row;

    fn build(row: Self::Row) -> Self {
        MyChronoTypeLoader(Option::<NaiveDateTime>::build(row).map(|d| d.format("%d/%m/%Y").to_string()))
    }
}
0 голосов
/ 25 апреля 2020

При использовании ORM данные извлекаются из базы данных в наиболее подходящем представлении для интерпретации ORM; после чего вы будете манипулировать им в целевом домене (в данном случае ржавчина).

Так как date_sale является Option<NaiveDateTime>, вы можете использовать параметры форматирования, предоставляемые chrono :

sale.date_sale.unwrap().format("%d/%m/%Y").to_string()

(Ваш реальный код не будет использовать, конечно, unwrap())

В качестве альтернативы, если вам действительно нужна база данных для форматирования, вы можете использовать [sql][2] для вставьте некоторые необработанные SQL в ваш запрос:

let results = sales::table.select((sales::id, sql("to_char(date_sale, 'dd/mm/YYYY')")))
    .load::<(i32, String)>(&conn);

Это было бы более полезно, если бы вы пытались реализовать некоторые логики c, которые было бы проще или эффективнее реализовать в SQL.

Примером может служить условие фильтрации: предположим, что вы хотите включить строки из своей таблицы, где номер недели был четным числом. Хотя вы можете загрузить всю таблицу и затем отфильтровать ее в области ржавчины, это не очень эффективно. Вместо этого вы можете сделать это:

let results = sales::table
    .filter(sql("extract(week from date_sale)::smallint % 2=0"))
    .load::<Sales>(&conn);
...