如果你使用 Drizzle-Kit 进行迁移,请确保导出架构文件中定义的所有模型,以便 Drizzle-Kit 可以导入它们并在迁移 diff 过程中使用它们。
Drizzle 模式
Drizzle 允许你使用 TypeScript 定义一个模式,其中包含底层数据库支持的各种模型和属性。定义模式后,它将作为未来查询(使用 Drizzle-ORM)和迁移(使用 Drizzle-Kit)修改的可靠来源。
组织你的模式文件
你可以直接在 TypeScript 中声明 SQL 模式,可以在单个 schema.ts
文件中声明,也可以将它们分散到各个地方 - 无论你喜欢哪种方式,自由自在!
单个文件中的 Schema
使用 Drizzle 声明模式的最常见方式是将所有表放入一个 schema.ts
文件中。
注意:你可以随意命名你的模式文件。例如,它可以是
models.ts
,或者其他内容。
如果你没有定义太多表模型,或者你可以接受将它们全部保存在一个文件中,则这种方法非常有效。
示例:
📦 <project root>
└ 📂 src
└ 📂 db
└ 📜 schema.ts
在 drizzle.config.ts
文件中,你需要指定模式文件的路径。使用此配置,Drizzle 将从 schema.ts
文件读取,并在迁移生成过程中使用此信息。有关 drizzle.config.ts
文件和使用 Drizzle 进行迁移的更多信息,请查看:link
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: 'postgresql', // 'mysql' | 'sqlite' | 'turso'
schema: './src/db/schema.ts'
})
多个文件中的 Schema
你不仅可以将 Drizzle 模型(例如表、枚举、序列等)放在一个文件中,还可以放在你喜欢的任何文件中。你唯一需要确保的是,从这些文件中导出所有模型,以便 Drizzle 套件可以导入它们并在迁移中使用它们。
一种用例是将每个表拆分成各自的文件。
📦 <project root>
└ 📂 src
└ 📂 db
└ 📂 schema
├ 📜 users.ts
├ 📜 countries.ts
├ 📜 cities.ts
├ 📜 products.ts
├ 📜 clients.ts
└ 📜 etc.ts
在 drizzle.config.ts
文件中,你需要指定模式文件夹的路径。使用此配置,Drizzle 将从 schema
文件夹读取,递归查找所有文件,并从中获取所有 Drizzle 表。有关 drizzle.config.ts
文件和使用 Drizzle 进行迁移的更多信息,请查看:link
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: 'postgresql', // 'mysql' | 'sqlite' | 'turso'
schema: './src/db/schema'
})
你还可以按任何你喜欢的方式对它们进行分组,例如为用户相关表、消息相关表、产品相关表等创建组。
📦 <project root>
└ 📂 src
└ 📂 db
└ 📂 schema
├ 📜 users.ts
├ 📜 messaging.ts
└ 📜 products.ts
在 drizzle.config.ts
文件中,你需要指定模式文件的路径。使用此配置,Drizzle 将从 schema.ts
文件读取,并在迁移生成过程中使用此信息。有关 drizzle.config.ts
文件和使用 Drizzle 进行迁移的更多信息,请查看:link
import { defineConfig } from "drizzle-kit";
export default defineConfig({
dialect: 'postgresql', // 'mysql' | 'sqlite' | 'turso'
schema: './src/db/schema'
})
调整数据模式
Drizzle 模式由你正在使用的数据库中的几种模型类型组成。使用 Drizzle,你可以指定:
-
包含列、约束等的表。
-
Schemas(PostgreSQL only)
-
枚举
-
Sequences(PostgreSQL only)
-
视图
-
物化视图
-
等等
让我们逐一检查如何在 Drizzle 中定义模式。
表和列声明
Drizzle 中的表应至少定义一列,与数据库中的表相同。有一件重要的事情需要注意,Drizzle 中不存在所谓的通用表对象。你需要选择你正在使用的方言:PostgreSQL、MySQL 或 SQLite
import { pgTable, integer } from "drizzle-orm/pg-core"
export const users = pgTable('users', {
id: integer()
});
默认情况下,Drizzle 将使用 TypeScript 键名作为数据库查询中的列名。因此,示例中的模式和查询将生成如下所示的 SQL 查询。
此示例使用了一个 db 对象,其初始化在本部分文档中未介绍。要了解如何连接数据库,请参阅 连接文档。
TypeScript 键 = 数据库键
// schema.ts
import { integer, pgTable, varchar } from "drizzle-orm/pg-core";
export const users = pgTable('users', {
id: integer(),
first_name: varchar()
})
// query.ts
await db.select().from(users);
SELECT "id", "first_name" from users;
如果你想在 TypeScript 代码和数据库中使用不同的名称,可以使用列别名。
// schema.ts
import { integer, pgTable, varchar } from "drizzle-orm/pg-core";
export const users = pgTable('users', {
id: integer(),
firstName: varchar('first_name')
})
// query.ts
await db.select().from(users);
SELECT "id", "first_name" from users;
驼峰式和蛇形式
数据库模型命名通常使用 snake_case
约定,而在 TypeScript 中,通常使用 camelCase
来命名模型。这会导致模式中出现大量别名定义。为了解决这个问题,Drizzle 提供了一种方法,通过在 Drizzle 数据库初始化期间包含一个可选参数,自动将 camelCase
从 TypeScript 映射到数据库中的 snake_case
对于此类映射,你可以在 Drizzle DB 声明中使用 casing
选项。此参数将帮助你指定数据库模型命名约定,并尝试相应地映射所有 JavaScript 键。
// schema.ts
import { drizzle } from "drizzle-orm/node-postgres";
import { integer, pgTable, varchar } from "drizzle-orm/pg-core";
export const users = pgTable('users', {
id: integer(),
firstName: varchar()
})
// db.ts
const db = drizzle({ connection: process.env.DATABASE_URL, casing: 'snake_case' })
// query.ts
await db.select().from(users);
SELECT "id", "first_name" from users;
高级
Drizzle ORM 的一些使用技巧可供参考。只要 Drizzle 完全基于 TypeScript 文件,你基本上就可以使用代码在简单的 TypeScript 项目中执行任何操作。
一个常见功能是将列分隔到不同的位置,然后重复使用它们。例如,考虑 updated_at
、created_at
和 deleted_at
列。许多表/模型可能需要这三个字段来跟踪和分析系统中实体的创建、删除和更新。
我们可以在单独的文件中定义这些列,然后将它们导入并分布到你拥有的所有表对象中。
// columns.helpers.ts
const timestamps = {
updated_at: timestamp(),
created_at: timestamp().defaultNow().notNull(),
deleted_at: timestamp(),
}
// users.sql.ts
export const users = pgTable('users', {
id: integer(),
...timestamps
})
// posts.sql.ts
export const posts = pgTable('posts', {
id: integer(),
...timestamps
})
Schemas
在 PostgreSQL 中,有一个称为 schema
的实体(我们认为应该称为 folders
)。这将在 PostgreSQL 中创建一个结构:
你可以使用 pgSchema
管理你的 PostgreSQL 模式,并将任何其他模型放置在其中。
定义要使用 Drizzle 管理的架构
import { pgSchema } from "drizzle-orm/pg-core"
export const customSchema = pgSchema('custom');
然后将表放置在模式对象中。
import { integer, pgSchema } from "drizzle-orm/pg-core";
export const customSchema = pgSchema('custom');
export const users = customSchema.table('users', {
id: integer()
})
示例
了解基础知识后,让我们为实际项目定义一个模式示例,以便更好地查看和理解。
所有示例都将使用
generateUniqueString
。它的实现将在所有架构示例之后提供。
import { AnyPgColumn } from "drizzle-orm/pg-core";
import { pgEnum, pgTable as table } from "drizzle-orm/pg-core";
import * as t from "drizzle-orm/pg-core";
export const rolesEnum = pgEnum("roles", ["guest", "user", "admin"]);
export const users = table(
"users",
{
id: t.integer().primaryKey().generatedAlwaysAsIdentity(),
firstName: t.varchar("first_name", { length: 256 }),
lastName: t.varchar("last_name", { length: 256 }),
email: t.varchar().notNull(),
invitee: t.integer().references((): AnyPgColumn => users.id),
role: rolesEnum().default("guest"),
},
(table) => [
t.uniqueIndex("email_idx").on(table.email)
]
);
export const posts = table(
"posts",
{
id: t.integer().primaryKey().generatedAlwaysAsIdentity(),
slug: t.varchar().$default(() => generateUniqueString(16)),
title: t.varchar({ length: 256 }),
ownerId: t.integer("owner_id").references(() => users.id),
},
(table) => [
t.uniqueIndex("slug_idx").on(table.slug),
t.index("title_idx").on(table.title),
]
);
export const comments = table("comments", {
id: t.integer().primaryKey().generatedAlwaysAsIdentity(),
text: t.varchar({ length: 256 }),
postId: t.integer("post_id").references(() => posts.id),
ownerId: t.integer("owner_id").references(() => users.id),
});
generateUniqueString
实现:
function generateUniqueString(length: number = 12): string {
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let uniqueString = "";
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
uniqueString += characters[randomIndex];
}
return uniqueString;
}
下一步是什么?
Manage schema
Zero to Hero