Ecto中一对一关系的归档方案问询
解决Ecto中Room与Actor的一对一归档关联问题
核心需求回顾
- 同一时间一个
Room仅能关联一个Active状态的Actor Actor离开房间后标记为Archived,需保留与原Room的关联用于归档- 必须支持通过
some_room.actor获取当前活跃Actor,通过some_actor.room获取关联的Room
解决方案实现
1. 调整Schema关联配置
修改Room的has_one关联,添加状态过滤条件;同时完善Actor的变更集逻辑:
# Actor Model defmodule MyApp.Actor do use Ecto.Schema import Ecto.Changeset schema "actors" do field :actor_name, :string field :status, Ecto.Enum, values: [:Active, :Archived], default: :Active belongs_to :room, MyApp.Room end def changeset(actor, attrs) do actor |> cast(attrs, [:actor_name, :status, :room_id]) |> validate_required([:actor_name, :status]) end end # Room Model defmodule MyApp.Room do use Ecto.Schema import Ecto.Changeset schema "rooms" do field :room_number, :integer # 仅关联Active状态的Actor,确保some_room.actor返回当前活跃实例 has_one :actor, MyApp.Actor, where: [status: :Active] end def changeset(room, attrs) do room |> cast(attrs, [:room_number]) |> validate_required([:room_number]) end end
2. 调整数据库外键约束
在actors表的迁移文件中,设置外键的on_delete行为,避免删除Room时破坏归档数据:
defmodule MyApp.Repo.Migrations.CreateActors do use Ecto.Migration def change do create table(:actors) do add :actor_name, :string, null: false add :status, :string, null: false # 当Room被删除时,将Actor的room_id设为nil(保留其他归档数据) add :room_id, references(:rooms, on_delete: :nilify_all) end # 添加索引优化查询 create index(:actors, [:room_id, :status]) end end
3. 实现Actor切换逻辑
通过事务确保原子性,归档旧Actor并关联新Actor:
defmodule MyApp.RoomActorManager do alias MyApp.{Repo, Room, Actor} def assign_actor(%Room{} = room, %Actor{} = new_actor) do Repo.transaction(fn -> # 1. 归档当前房间的活跃Actor(保留room_id) if current_actor = Repo.get_by(Actor, room_id: room.id, status: :Active) do current_actor |> Actor.changeset(%{status: :Archived}) |> Repo.update!() end # 2. 将新Actor关联到房间并设为活跃状态 new_actor |> Actor.changeset(%{room_id: room.id, status: :Active}) |> Repo.insert_or_update!() # 返回更新后的Room(预加载当前活跃Actor) Repo.preload(room, :actor) end) end end
关键逻辑说明
Room的has_one关联添加where: [status: :Active],确保预加载或查询时仅返回当前活跃的Actor,满足some_room.actor的访问需求- 切换Actor时通过事务保证原子性,避免数据不一致
- 旧Actor仅修改状态,保留
room_id字段,确保some_actor.room仍能访问原关联房间 - 外键设置
on_delete: :nilify_all,防止删除Room时连带删除归档的Actor数据
内容的提问来源于stack exchange,提问作者DaveIdito
相关产品推荐
相关产品推荐

