如何使用Ecto实现用户最多一个活跃Item的约束?
实现每个用户最多一个活跃Item的约束
要实现这个需求,确实可以通过Ecto的exclusion_constraint结合数据库级别的排除约束完成,具体步骤如下:
1. 添加数据库排除约束
首先创建数据库迁移,给items表添加带条件的排除约束,仅对active = true的记录生效:
defmodule MyApp.Repo.Migrations.AddActiveItemExclusionConstraint do use Ecto.Migration def change do # 添加约束:当active为true时,同一个user_id只能存在一条记录 execute """ ALTER TABLE items ADD CONSTRAINT one_active_item_per_user EXCLUDE USING gist ( user_id WITH =, (active = true) WITH = ) WHERE (active = true); """, # 回滚时删除约束 """ ALTER TABLE items DROP CONSTRAINT one_active_item_per_user; """ end end
注:这里使用PostgreSQL的gist索引类型(Ecto的排除约束主要适配PostgreSQL),WHERE (active = true)确保只有活跃项目受约束,非活跃项目不受限制。
2. 在Item Changeset中配置约束捕获
修改Item模块的changeset,添加exclusion_constraint来捕获数据库的约束违反错误,并转为友好提示:
defmodule MyApp.Item do use Ecto.Schema import Ecto.Changeset schema "items" do field(:active, :boolean) belongs_to(:user, MyApp.User) end def changeset(item, attrs) do item |> cast(attrs, [:active, :user_id]) |> validate_required([:active, :user_id]) # 关联数据库约束,指定错误提示 |> exclusion_constraint(:user_id, name: :one_active_item_per_user, message: "每个用户只能拥有一个活跃项目") end end
验证效果
- 给同一个用户插入第二条
active: true的Item时,changeset会返回包含上述错误信息的验证失败结果 - 插入任意数量
active: false的Item均可正常执行 - 尝试将用户的非活跃Item更新为活跃状态时,若该用户已有活跃Item,同样会触发错误
内容的提问来源于stack exchange,提问作者martasd
相关产品推荐
相关产品推荐

