我想使用相同的结构从
sqlx
接收数据并将该数据传递到 askama::Template
。 (我会有很多这样的结构。)
sqlx
让我使用 Option<String>
,因为该列是可为空的 varchar。
askama::Template
禁止我使用 Option<String>
,因为它需要 Display
特征。
如果我尝试从包含
askama::Template
的结构中派生 Option<String>
,我会收到错误:
error[E0277]: `std::option::Option<std::string::String>` doesn't implement `std::fmt::Display`
--> src/root.rs:35:10
|
35 | #[derive(Template)]
| ^^^^^^^^ `std::option::Option<std::string::String>` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `std::option::Option<std::string::String>`, which is required by `&std::option::Option<std::string::String>: std::fmt::Display`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: this error originates in the macro `$crate::format_args` which comes from the expansion of the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)
如何将一个结构体用于这两个目的?
这是我当前的解决方法,使用两个类似的结构和
From
的实现:
use askama::Template;
use axum::{extract::Query, Extension};
use chrono::{DateTime, Utc};
use serde::Deserialize;
use sqlx::PgPool;
//use tracing::debug;
#[derive(sqlx::FromRow, Debug)]
pub struct LatestProvisioningRequest {
uid: String,
ts: DateTime<Utc>,
ip: Option<String>,
}
pub struct LatestProvisioningRequestForTemplate {
uid: String,
ts: DateTime<Utc>,
ip: String,
}
impl From<LatestProvisioningRequest> for LatestProvisioningRequestForTemplate {
fn from(value: LatestProvisioningRequest) -> Self {
let ip = match value.ip {
Some(str) => str,
None => "".to_owned(),
};
LatestProvisioningRequestForTemplate {
uid: value.uid,
ts: value.ts,
ip
}
}
}
#[derive(Template)]
#[template(path = "root.html")]
pub struct RootTemplate {
requests: Vec<LatestProvisioningRequestForTemplate>
}
// Used to extract the "next" field from the query string.
#[derive(Debug, Deserialize)]
pub struct RootQueryParameters {
// TODO
}
pub async fn fetch_unenrolled(pool: &PgPool) -> Vec<LatestProvisioningRequestForTemplate> {
let rows = sqlx::query_as::<_, LatestProvisioningRequest>(r#"SELECT * FROM latest_provisioning_requests"#)
.fetch_all(pool)
.await
.unwrap();
rows.into_iter().map(|x| x.into()).collect()
}
pub async fn root(
Extension(pool): Extension<PgPool>,
Query(RootQueryParameters {}): Query<RootQueryParameters>
) -> RootTemplate {
RootTemplate {
requests: fetch_unenrolled(&pool).await
}
}
为了让一切保持简单,我们假设我们有以下内容
struct Page
:
use askama::Template;
#[derive(Template, Clone, Debug)]
#[template(path = "page.html.jinja")]
pub struct Page {
text: Option<String>,
}
现在我们希望始终渲染
text
(如果是 Some
)或渲染 无(如果是 None
)。
我们可以通过几种方式做到这一点,您可以使用
match
:
{% match self.text %}
{% when Some with (text) %}
{{ text }}
{% else %}
{% endmatch %}
您还可以实现一个实用程序
fn display_some()
并调用它:
pub fn display_some<T>(value: &Option<T>) -> String
where
T: std::fmt::Display,
{
value
.as_ref()
.map(|value| value.to_string())
.unwrap_or_default()
}
请注意,这是故意使用
&Option<T>
,而不是 Option<T>
或 Option<&T>
,Askama 自动参考值。
模板:
{{ self::display_some(self.text) }}
您还可以实现自定义过滤器,但我刚刚遇到了一个我不认识的问题。我是 Askama 的(被动)维护者之一,所以我会调查一下。