Wasp 数据模型入门(v0.13):Entity 实体定义、Prisma 映射与直接操作指南
【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp
Entity(实体)是 Wasp 应用数据模型的地基:一个 Entity 就对应数据库中的一个数据模型。本文基于 Wasp v0.13 版本文档 entities.md 展开,完整覆盖实体的声明语法({=psl ... psl=}内嵌 Prisma Schema Language)、wasp db migrate-dev迁移工作流、通过 Operations 与 Prisma Client 访问实体的两种方式,并结合编译器源码(Haskell 实现的 PSL 解析器与实体 AST)解释 Wasp 是如何理解你的实体定义、识别主键并支撑自动 CRUD 的。
1. Entity 与 Prisma:Wasp 数据层的底座
Wasp 用 Prisma ORM 实现全部数据库功能,并在其上覆盖一层薄薄的抽象。核心关系是:Wasp Entity 与 Prisma 的数据模型(data model)一一对应。
这带来三个直接结论:
- 你不需要先精通 Prisma。Wasp 为 Prisma 的核心能力封装了简单 API(Operations、CRUD 等),绝大多数场景不用直接接触 Prisma Client 即可读写数据。
- 定义实体的唯一前置技能是 PSL(Prisma Schema Language)——Prisma 专门用于声明式定义模型的简单语言。PSL 声明式且直白,读到下文示例就能上手,无需提前系统学习。
- 数据库能力的边界由 Prisma 决定。需要 Wasp 没有封装的高级能力(复杂事务、原始查询等)时,可以“降级”直接拿 Prisma Client,这在 v0.13 中是被官方认可的路径。
2. 定义一个 Entity:entity Task的完整解剖
entity声明即数据库模型声明。以最常见的Task(任务)为例,v0.13 中在项目的.wasp文件(如main.wasp)中这样写:
entity Task {=psl id Int @id @default(autoincrement()) description String isDone Boolean @default(false) psl=}逐条拆解这份声明:
entity Task:告诉 Wasp 我们要定义一个名为Task的实体(即数据库模型)。Wasp 会自动创建名为tasks(复数小写)的表,这一点在后续 Operations 路由生成中同样生效(详见第 5 节源码佐证)。{=psl ... psl=}:Wasp 把两个psl标记之间的内容当作PSL(Prisma Schema Language)处理。也就是说,实体定义 = Wasp 的entity外壳 + 原生的 Prisma 模型字段语法,Prisma 的属性(attribute)语义在这里完整保留。
上面的 PSL 定义了tasks表的三列:
| 字段 | 类型 | 属性 | 含义 |
|---|---|---|---|
id | Int | @id @default(autoincrement()) | 主键,整型。数据库自动生成:在上一行自增 1 |
description | String | 无 | 任务描述,普通字符串列 |
isDone | Boolean | @default(false) | 完成状态布尔列。创建时不显式设置,数据库默认置false |
几个可以直接复用的 PSL 要点:
@id标记主键字段——Wasp 编译器对它的识别逻辑有源码级证据,见第 5 节;@default(...)提供列级默认值,支持autoincrement()、false、now()、uuid()等 Prisma 默认值表达式;- 字段名、类型与 Prisma 保持原样(
Int、String、Boolean、DateTime、Float、Json等),因此你已有的 Prisma 建模经验可以平移过来。
一个现实项目里实体长什么样,可以参考仓库中示例应用 kitchen-sink 的数据模型 schema.prisma(注意:该示例属于采用新版schema.prisma文件定义方式的仓库,字段语法与 v0.13 内嵌 PSL 完全同构):
model Task { id Int @id @default(autoincrement()) description String isDone Boolean @default(false) user User @relation(fields: [userId], references: [id]) userId Int votes TaskVote[] visibility TaskVisibility @default(PRIVATE) }可以看到,除单列字段外,模型中还出现了关联(@relation)、反向关系列表(TaskVote[])与枚举默认值(@default(PRIVATE))——这些全部是标准 PSL,定义方式与Task三字段示例一脉相承。
3. 从定义到建表:四步工作流
定义实体只是第一步,v0.13 文档给出的完整落地流程是:
- 在
.wasp文件中创建/更新实体——即上节的entity声明。 - 运行
wasp db migrate-dev。该命令负责把数据库与.wasp文件中的实体定义同步起来,实现方式是生成迁移脚本(migration scripts):它对比目标模型与当前库,产出描述差异的 SQL 迁移。 - 迁移脚本会自动落在
migrations/目录,务必将该目录提交进版本控制——迁移历史是团队协作与部署的基础。 - 在实现 Operations 时,用 Wasp 的 JavaScript API 操作数据库——即 Query/Action,v0.13 文档将此留给了 operations 章节。
关于第 2、3 步有两条实践边界:
- 数据库连接是前提。
wasp db migrate-dev需要可连的数据库,migrations/目录里每个迁移带时间戳前缀(如20231124161113_initial)与.sql文件,可参考 migrations 目录 与其中的 migration_lock.toml(锁定 Prisma 迁移引擎方言)。 - 切换数据库系统后必须重新迁移。v0.13 支持 SQLite(默认)与 PostgreSQL,生产部署需 PostgreSQL;由于迁移脚本与方言绑定,切换系统后要删除旧
migrations/并重新wasp db migrate-dev(参见同版本 Databases 文档 的 “Migrating from SQLite to PostgreSQL” 一节)。
4. 使用实体:Operations 优先,Prisma Client 兜底
4.1 在 Operations 中使用实体(推荐路径)
绝大多数时候,实体是在Operations(Query & Action)的上下文中被使用的——Query 读、Action 写,且带有权限(public/private)语义。v0.13 文档将细节放在了 operations/overview 与 crud。
这一路径背后的机制在编译器中可以直接看到:实体(含其主键字段)会被转换成 CRUD 元数据,进而生成crud/<entityLower>/get、crud/tasks/get-all、crud/tasks/create等标准路由。源码测试 CrudTest.hs 固化了这份元数据的确切形状:
[ "name" .= crudOperationsName, -- "tasks" "operations" .= object operations, "entitiesArray" .= ("['Task']" :: String), "idFieldName" .= ("id" :: String), "entityLower" .= ("task" :: String), "entityUpper" .= ("Task" :: String) ]idFieldName来自实体的主键字段,entityLower/entityUpper即第 2 节提到的自动复数/单数表名规则的直接体现:你只需要声明entity Task,Operations 路由、参数命名都由编译器派生。
4.2 直接使用 Prisma Client(需要更多控制时)
当 Wasp 封装的能力不够(复杂查询、原生 SQL、特殊事务语义等),可以绕过 Operations,直接 import Prisma Client。官方建议仍是:优先走 Wasp 机制,仅在需要 Wasp 未提供的特性时才直接操作 Prisma Client。
限制条件:Prisma Client 只能在 Wasp 的服务端代码中使用(Action、Job、server-only 模块等),客户端代码不可用。用法:
import { prisma } from 'wasp/server' prisma.task.create({ description: "Read the Entities doc", isDone: true // almost :) })注意prisma.task——实体模型名Task在这里变为 Prisma Client 上的小驼峰代理task,与表名tasks、entityLower: "task"的命名链完全一致(见 4.1 的元数据)。TypeScript 版本代码相同,类型由生成的 Prisma Client 提供。
5. 源码视角:Wasp 如何解析你的实体定义
结合仓库中 v0.13 编译器(Haskell,waspc包)的源码结构,可以看清entity声明从文本到数据库动作的完整链路:
(1)PSL 被当作一等语法解析。Wasp 自带一套 Prisma Schema Language 的 AST、解析器与代码生成器,模块布局见 wasp-cli 的 Psl 源码目录:
Psl/Ast/Model.hs定义了模型 AST:
data Model = Model Name Body data Field = Field { _name :: String, _type :: FieldType, _typeModifiers :: [FieldTypeModifier], _attrs :: [Attribute] }对照第 2 节的示例:id Int @id @default(autoincrement())会被解析为Field {_name = "id", _type = Int, _attrs = [@id, @default(autoincrement())]}。内置字段类型由 FieldType 枚举覆盖:String、Boolean、Int、BigInt、Float、Decimal、DateTime、Json、Bytes,另支持UserType(关联到其他模型)与Unsupported(兜底未知类型);FieldTypeModifier 则对应[](列表)与?(可选)修饰符。这意味着 v0.13 对实体内部的字段类型、属性做了完整结构化理解,而不是简单的字符串透传。
(2)Entity 是一等 AppSpec 构件。Entity.hs 将实体建模为“包裹 PSL 模型体的 newtype”,并提供getFields、getIdField等访问器:
newtype Entity = Entity { pslModelBody :: Psl.Model.Body }(3)主键识别有明确的判定规则。Psl/Util.hs 中,findIdField的判定条件就是“字段带有@id属性”(源码注释原文:We define an ID field as a field that has the @id attribute),同时findIdBlockAttribute支持@@id复合主键块属性。这个识别结果正是 4.1 中 CRUD 元数据idFieldName字段的来源,也解释了为什么每个实体必须有一个可定位的主键——它是 Operations/CRUD 生成按主键读取、更新、删除逻辑的依据。
(4)一个版本演进注记。从源码结构看,Entity 的 JSON 反序列化已被有意废弃(Entity.hs 的FromJSON直接返回失败信息:“entities are now defined via prisma.schema file”)。也就是说,在 Wasp 后续版本中实体定义从.wasp内嵌 PSL 迁移到了独立的schema.prisma文件(当前主干的 examples 各示例 均已如此)。阅读 v0.13 文档时请以“entity {=psl ... psl=}”为准;升级后只需把同一份 PSL 内容移入schema.prisma的model块,字段与属性语法不变。
6. 小结
- 定义:
entity Task {=psl ... psl=}= Wasp 外壳 + 原生 PSL;表名自动取复数(tasks),@id标记主键,@default给出列默认值。 - 落库:
wasp db migrate-dev生成迁移脚本并写入migrations/(提交它);换数据库系统需删库重建迁移。 - 使用:首选 Operations(Query/Action),底层由编译器从实体元数据(主键、单复数名)派生 CRUD 路由;需要细粒度控制时,在服务端
import { prisma } from 'wasp/server'直接操作 Prisma Client。 - 原理:Wasp 编译器对 PSL 做了完整 AST 级解析(
Psl/Ast/Model.hs),并按@id/@@id规则识别主键(Psl/Util.hs),这是 Operations 与自动 CRUD 能够按实体生成的基础。
完成实体定义之后,下一步自然是学习如何用 Operations 高效地读写它们:继续阅读 v0.13 版 Operations 总览 与 CRUD 文档。
【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考