You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 07:43:09