咨询Ecto中ActiveRecord `has_many :through`的`source`选项替代方案
Ecto中替代ActiveRecord
has_many :through的:source选项方案 核心思路
Ecto没有直接对应Rails的:source选项,但可以通过明确中间表的关联名称实现相同效果。核心是在连接表(AttendingEvents)中定义指向目标Schema的命名关联,再在主表的has_many :through关联中引用这个命名。
完整Schema实现
1. 连接表 AttendingEvents
定义连接表Schema,明确与User的关联名为:attendee(对应Rails中:source指向的关联),并指定外键:
defmodule MyApp.AttendingEvents do use Ecto.Schema import Ecto.Changeset schema "attending_events" do # 关联参会用户,外键为attendee_id belongs_to :attendee, MyApp.User, foreign_key: :attendee_id # 关联所属活动 belongs_to :event, MyApp.Event timestamps() end def changeset(attending_events, attrs) do attending_events |> cast(attrs, [:attendee_id, :event_id]) |> validate_required([:attendee_id, :event_id]) |> unique_constraint([:attendee_id, :event_id]) # 避免重复报名 end end
2. Event Schema(核心需求实现)
在Event中定义通过连接表关联参会用户的关系,直接引用连接表中定义的:attendee关联:
defmodule MyApp.Event do use Ecto.Schema import Ecto.Changeset schema "events" do # 关联活动创建者(假设Event表有owner_id外键) belongs_to :owner, MyApp.User, foreign_key: :owner_id # 关联连接表记录 has_many :attending_events, MyApp.AttendingEvents # 通过连接表关联参会用户,等价于Rails的source: :user has_many :attendees, through: [:attending_events, :attendee] timestamps() end # 省略changeset实现 end
3. User Schema(补充完整关联)
完善User侧的关联,实现查看用户参与的活动:
defmodule MyApp.User do use Ecto.Schema import Ecto.Changeset schema "users" do # 关联用户创建的活动 has_many :owned_events, MyApp.Event, foreign_key: :owner_id # 关联连接表记录(外键为attendee_id) has_many :attending_events, MyApp.AttendingEvents, foreign_key: :attendee_id # 通过连接表查看用户参与的活动 has_many :attended_events, through: [:attending_events, :event] timestamps() end # 省略changeset实现 end
使用验证
现在你可以直接执行预加载操作,无需额外查询:
event = MyApp.Repo.get(MyApp.Event, id) |> MyApp.Repo.preload(:attendees) event.attendees # 直接获取该活动的所有参会用户
关键差异说明
- Rails中用
:source指定连接表中对应的关联名称,Ecto则直接在through选项的数组中传入连接表的关联名(这里是:attendee),效果完全等价。 - 所有关联的外键都需要明确指定,确保Ecto能正确解析表之间的关联关系。
内容的提问来源于stack exchange,提问作者Linda Kadz
相关产品推荐
相关产品推荐

