如何在Prisma Schema中实现用户仅一个默认地址的约束?
实现用户默认地址唯一性约束的方案
要满足「每个用户只能有一个isDefault = true的地址,同时允许多个非默认地址」的需求,需要从数据库约束和Prisma Schema定义两方面修改,以下是具体步骤:
1. 修改Address模型,添加isDefault字段
首先在Address模型中新增isDefault字段,标记该地址是否为用户默认地址,默认值设为false:
model Address { id Int @id @default(autoincrement()) addressLine1 String addressLine2 String? phoneNumber String addressName String email String country String state String name String postalCode String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // 新增默认地址标记字段 isDefault Boolean @default(false) userId String @db.Uuid user User @relation(fields: [userId], references: [id]) }
2. 添加数据库层面的唯一约束
核心是确保同一个用户下只能有一条isDefault = true的记录,不同数据库的实现方式略有差异:
针对PostgreSQL(推荐)
PostgreSQL支持「部分唯一索引」,可直接针对isDefault = true的记录添加联合唯一约束:
model Address { // ... 其他字段保持不变 isDefault Boolean @default(false) userId String @db.Uuid user User @relation(fields: [userId], references: [id]) // 部分唯一索引:仅当isDefault为true时,同一个userId只能有一条记录 @@unique([userId], map: "unique_user_default_address") where (isDefault = true) }
针对MySQL
MySQL不支持部分唯一索引,但可以通过「函数式唯一索引」间接实现:
model Address { // ... 其他字段保持不变 isDefault Boolean @default(false) userId String @db.Uuid user User @relation(fields: [userId], references: [id]) // 利用IF函数,将isDefault=true映射为1,false映射为NULL;NULL不参与唯一约束 @@unique([userId, @@raw("IF(isDefault, 1, NULL)")], map: "unique_user_default_address") }
原理:MySQL的唯一约束会忽略NULL值,因此同一个用户可以有多条isDefault=false的记录(此时函数返回NULL,不触发唯一校验),但只能有一条isDefault=true的记录(函数返回1,与userId组合唯一)。
3. Prisma客户端层面的辅助验证
虽然数据库约束已经能保证数据一致性,但在业务代码中可以提前做验证,避免触发数据库错误:
- 设置新的默认地址时,先查询该用户是否已有默认地址,若有则先将其设为
isDefault=false - 创建地址时,若标记为默认地址,同样先检查用户的默认地址状态
修改后的完整Schema示例(PostgreSQL版本)
model User { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid name String email String @unique password String phoneNumber String? @db.VarChar(50) isActive Boolean @default(false) isAdmin Boolean @default(false) resetCode String? @unique createdAt DateTime @default(now()) updatedAt DateTime @updatedAt addresses Address[] @@index([email]) } model Address { id Int @id @default(autoincrement()) addressLine1 String addressLine2 String? phoneNumber String addressName String email String country String state String name String postalCode String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt isDefault Boolean @default(false) userId String @db.Uuid user User @relation(fields: [userId], references: [id]) @@unique([userId], map: "unique_user_default_address") where (isDefault = true) }
内容的提问来源于stack exchange,提问作者Mohammad ARZ
相关产品推荐
相关产品推荐

