深入Schema
在本指南中,我们将看看究竟diesel print-schema和table! 。对于table!,我们将展示生成的实际代码的简化版本,并解释每个部分与您的相关性。如果您曾经对生成的内容或者use schema :: posts :: dsl :: *的含义感到困惑,那么这是正确的选择。
diesel print-schema是Diesel CLI提供的命令。此命令将建立数据库连接,查询所有表及其列的列表,并生table!每个的调用。 diesel print-schema将跳过任何以__(双下划线)开头的表名。可以将Diesel配置为在运行迁移时自动重新运行diesel print-schema。有关详细信息,请参阅配置Configuring Diesel CLI
table!是生成大量代码的地方。如果你愿意,你可以看到通过运行cargo rustc -- -Z unstable-options --pretty=expanded生成的实际确切代码.但是,输出会非常嘈杂,并且有很多代码实际上与您无关。相反,我们将逐步完成此输出的简化版本,该版本仅包含您将直接使用的代码。
对于此示例,我们将查看此table!生成的代码调用:
table! {users {id -> Integer,name -> Text,hair_color -> Nullable<Text>,}}
如果您只想查看完整的简化代码并亲自查看,您可以在本指南的最后找到它。
table!的输出 始终是具有相同名称的Rust模块。 该模块的第一个也是最重要的部分是表格本身的定义:
pub struct table;
这是用于构造SQL查询的表示用户表的结构。 它通常在代码中被引用为users :: table(或者有时只是users,稍微多一点)。 接下来,我们将看到一个名为columns的模块,每列的一个结构。
pub struct id;pub struct name;pub struct hair_color;
为了构造SQL查询,这些结构中的每一个都唯一地表示表的每一列。 这些结构中的每一个都将实现一个名为Expression的特征,该特征指示列的SQL类型。
impl Expression for id {type SqlType = Integer;}impl Expression for name {type SqlType = Text;}impl Expression for hair_color {type SqlType = Nullable<Text>;}
SqlType类型是Diesel如何确保您的查询正确的核心。 ExpressionMethods将使用此类型来确定哪些内容可以和不可以传递给方法,如eq。 Queryable还将使用它来确定当该列出现在select子句中时可以反序列化的类型。
在列模块中,您还会看到一个名为star的特殊列。 它的定义如下:
pub struct star;impl Expression for star {type SqlType = ();}
star结构表示查询构建器中的users.*。 此结构仅用于生成计数查询。 它永远不应该直接使用。 Diesel通过索引而不是按名称从查询加载数据。 为了确保我们实际获得我们认为的列的数据,当我们真正想要从中获取数据时,Diesel从不使用*。 我们将生成一个显式的select子句,例如SELECT users.id,users.name,users.hair_color。
列模块中的所有内容都将从父模块重新导出。 这就是为什么我们可以将列引用为users :: id,而不是users :: columns :: id。
pub use self::columns::*;pub struct table;pub mod columns {/* ... */}
当所有内容都必须以users ::为前缀时,查询通常会变得非常冗长。 出于这个原因,Diesel还提供了一个名为dsl的便利模块。
pub mod dsl {pub use super::columns::{id, name, hair_color};pub use super::table as users;}
此模块重新导出columns中的所有内容(star除外),并重新导出表但重命名为表的实际名称。 这意味着不是写作
users::table.filter(users::name.eq("Sean")).filter(users::hair_color.eq("black"))
我们可以改写
users.filter(name.eq("Sean")).filter(hair_color.eq("black"))
只应为单个函数导入dsl模块。 你永远不应该使用schema :: users :: dsl :: *; 在模块的顶部。 像#[derive(Insertable)]这样的代码将假定users指向模块,而不是表结构。
如果您使用schema :: users :: dsl :: *;则由于star无法访问,因此它也会在表格上公开为实例方法。
impl table {pub fn star(&self) -> star {star}}
接下来,为table实现了几个特性。 您通常永远不会直接与这些进行交互,但它们可以启用query_dsl中的大多数查询构建器函数,以及与insert,update和delete一起使用。
impl AsQuery for table {/* body omitted */}impl Table for table {/* body omitted */}impl IntoUpdateTarget for table {/* body omitted */}
最后,定义了一些小类型定义和常量,以使您的生活更轻松。
pub const all_columns: (id, name, hair_color) = (id, name, hair_color);pub type SqlType = (Integer, Text, Nullable<Text>);pub type BoxedQuery<'a, DB, ST = SqlType> = BoxedSelectStatement<'a, ST, table, DB>;
all_columns只是表中所有列的元组。 当您未明确指定查询时,它用于为此表上的查询生成select语句。 如果你想引用users :: star来查找不是count查询的东西,你可能需要users::all_columns 。
SqlType将是all_columns的SQL类型。 很少需要直接引用它,但是当需要时,它比<< users :: table as Table> :: AllColumns as Expression> :: SqlType更简洁.
最后,有一个帮助器类型用于引用从该表构建的盒装查询。 这意味着代替编写BoxedSelectStatement <'static,users :: SqlType,users :: table,Pg>而不是编写users :: BoxedQuery <'static,Pg>。 如果查询具有自定义select子句,您也可以选择指定SQL类型。
这就是一切! 以下是为此表生成的完整代码:
pub mod users {pub use self::columns::*;pub mod dsl {pub use super::columns::{id, name, hair_color};pub use super::table as users;}pub const all_columns: (id, name, hair_color) = (id, name, hair_color);pub struct table;impl table {pub fn star(&self) -> star {star}}pub type SqlType = (Integer, Text, Nullable<Text>);pub type BoxedQuery<'a, DB, ST = SqlType> = BoxedSelectStatement<'a, ST, table, DB>;impl AsQuery for table {/* body omitted */}impl Table for table {/* body omitted */}impl IntoUpdateTarget for table {/* body omitted */}pub mod columns {pub struct star;impl Expression for star {type SqlType = ();}pub struct id;impl Expression for id {type SqlType = Integer;}pub struct name;impl Expression for name {type SqlType = Text;}pub struct hair_color;impl Expression for hair_color {type SqlType = Nullable<Text>;}}}
