深入Schema

在本指南中,我们将看看究竟diesel print-schematable! 。对于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!生成的代码调用:

  1. table! {
  2. users {
  3. id -> Integer,
  4. name -> Text,
  5. hair_color -> Nullable<Text>,
  6. }
  7. }

如果您只想查看完整的简化代码并亲自查看,您可以在本指南的最后找到它。

table!的输出 始终是具有相同名称的Rust模块。 该模块的第一个也是最重要的部分是表格本身的定义:

  1. pub struct table;

这是用于构造SQL查询的表示用户表的结构。 它通常在代码中被引用为users :: table(或者有时只是users,稍微多一点)。 接下来,我们将看到一个名为columns的模块,每列的一个结构。

  1. pub struct id;
  2. pub struct name;
  3. pub struct hair_color;

为了构造SQL查询,这些结构中的每一个都唯一地表示表的每一列。 这些结构中的每一个都将实现一个名为Expression的特征,该特征指示列的SQL类型。

  1. impl Expression for id {
  2. type SqlType = Integer;
  3. }
  4. impl Expression for name {
  5. type SqlType = Text;
  6. }
  7. impl Expression for hair_color {
  8. type SqlType = Nullable<Text>;
  9. }

SqlType类型是Diesel如何确保您的查询正确的核心。 ExpressionMethods将使用此类型来确定哪些内容可以和不可以传递给方法,如eqQueryable还将使用它来确定当该列出现在select子句中时可以反序列化的类型。

在列模块中,您还会看到一个名为star的特殊列。 它的定义如下:

  1. pub struct star;
  2. impl Expression for star {
  3. type SqlType = ();
  4. }

star结构表示查询构建器中的users.*。 此结构仅用于生成计数查询。 它永远不应该直接使用。 Diesel通过索引而不是按名称从查询加载数据。 为了确保我们实际获得我们认为的列的数据,当我们真正想要从中获取数据时,Diesel从不使用*。 我们将生成一个显式的select子句,例如SELECT users.id,users.name,users.hair_color

列模块中的所有内容都将从父模块重新导出。 这就是为什么我们可以将列引用为users :: id,而不是users :: columns :: id

  1. pub use self::columns::*;
  2. pub struct table;
  3. pub mod columns {
  4. /* ... */
  5. }

当所有内容都必须以users ::为前缀时,查询通常会变得非常冗长。 出于这个原因,Diesel还提供了一个名为dsl的便利模块。

  1. pub mod dsl {
  2. pub use super::columns::{id, name, hair_color};
  3. pub use super::table as users;
  4. }

此模块重新导出columns中的所有内容(star除外),并重新导出表但重命名为表的实际名称。 这意味着不是写作

  1. users::table
  2. .filter(users::name.eq("Sean"))
  3. .filter(users::hair_color.eq("black"))

我们可以改写

  1. users.filter(name.eq("Sean")).filter(hair_color.eq("black"))

只应为单个函数导入dsl模块。 你永远不应该使用schema :: users :: dsl :: *; 在模块的顶部。 像#[derive(Insertable)]这样的代码将假定users指向模块,而不是表结构。

如果您使用schema :: users :: dsl :: *;则由于star无法访问,因此它也会在表格上公开为实例方法。

  1. impl table {
  2. pub fn star(&self) -> star {
  3. star
  4. }
  5. }

接下来,为table实现了几个特性。 您通常永远不会直接与这些进行交互,但它们可以启用query_dsl中的大多数查询构建器函数,以及与insert,updatedelete一起使用。

  1. impl AsQuery for table {
  2. /* body omitted */
  3. }
  4. impl Table for table {
  5. /* body omitted */
  6. }
  7. impl IntoUpdateTarget for table {
  8. /* body omitted */
  9. }

最后,定义了一些小类型定义和常量,以使您的生活更轻松。

  1. pub const all_columns: (id, name, hair_color) = (id, name, hair_color);
  2. pub type SqlType = (Integer, Text, Nullable<Text>);
  3. 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类型。

这就是一切! 以下是为此表生成的完整代码:

  1. pub mod users {
  2. pub use self::columns::*;
  3. pub mod dsl {
  4. pub use super::columns::{id, name, hair_color};
  5. pub use super::table as users;
  6. }
  7. pub const all_columns: (id, name, hair_color) = (id, name, hair_color);
  8. pub struct table;
  9. impl table {
  10. pub fn star(&self) -> star {
  11. star
  12. }
  13. }
  14. pub type SqlType = (Integer, Text, Nullable<Text>);
  15. pub type BoxedQuery<'a, DB, ST = SqlType> = BoxedSelectStatement<'a, ST, table, DB>;
  16. impl AsQuery for table {
  17. /* body omitted */
  18. }
  19. impl Table for table {
  20. /* body omitted */
  21. }
  22. impl IntoUpdateTarget for table {
  23. /* body omitted */
  24. }
  25. pub mod columns {
  26. pub struct star;
  27. impl Expression for star {
  28. type SqlType = ();
  29. }
  30. pub struct id;
  31. impl Expression for id {
  32. type SqlType = Integer;
  33. }
  34. pub struct name;
  35. impl Expression for name {
  36. type SqlType = Text;
  37. }
  38. pub struct hair_color;
  39. impl Expression for hair_color {
  40. type SqlType = Nullable<Text>;
  41. }
  42. }
  43. }