在插入Ecto之前查询记录(类似于AR回调)

时间:2019-03-08 02:16:13

标签: callback elixir phoenix-framework ecto

我是Elixir和Phoenix的新手(不到10天),但对此感到非常兴奋,就像其他许多人一样,我都来自Rails背景。

我了解Ecto不是AR,并且不建议使用或删除回调,但是我需要添加自定义验证,该验证仅在创建时发生,并且需要执行查询。

这是我的Reservation模型的基本样子。

schema "reservations" do
  field :ends_at, :utc_datetime
  field :name, :string, null: false
  field :starts_at, :utc_datetime
  field :user_id, :id
end

然后有另一个架构Slot,它看起来像这样:

schema "slots" do
  field :ends_at, :utc_datetime
  field :name, :string, null: false
  field :starts_at, :utc_datetime
  field :admin_id, :id
end

无论何时添加新的保留,我都需要查询数据库以检查是否有匹配ends_atstarts_at的插槽。如果有的话,我需要防止保存记录并向其中添加错误(类似于我们在throw :aborterrors.add中完成的Rails操作)。

有人可以阐明这一点吗? Ecto这样做的方式是什么?

最诚挚的问候

1 个答案:

答案 0 :(得分:1)

* edit:添加了使用单独的变更集进行创建和更新的示例

您可以在变更集验证链中添加自定义验证功能,并在其中进行数据库查询。

没有运行这段代码,但是类似的东西应该起作用

# separate changeset for creation
def create_changeset(struct, params) do
  struct
  |> cast(params, [...list of fields...])
  |> validate_unique([:name]) # lets say it has to be unique
  |> validate_slots # -- custom validation
end

# separate changeset for updation, no slot-check
def update_changeset(struct, params) do
  struct
  |> cast(params, [...list of fields...])
  |> validate_unique([:name]) # lets say it has to be unique
end


def validate_slots(changeset) do
    starts_at = get_field(changeset, :starts_at)
    ends_at = get_field(changeset, :ends_at)
    slots = Repo.all(from s in Slot, where: s.starts_at == ^starts_at and s.ends_at == ^ends_at)

    if Enum.empty?(slots) do
      changeset
    else
      add_error( changeset, :starts_at, "has slot with similar starts_at/ends_at")
    end
end

#---- using the changesets
# creation
%Reservation{} |> Reservation.create_changeset(params) |> Repo.insert()

# updation
%Reservation{} |> Reservation.update_changeset(params) |> Repo.update()

尽管从外观上看,您可能应该将starts_at和ends_at标准化为一个单独的表,该表称为booking_time_frame或其他内容,并为其添加唯一索引。

或者您可能会获得更多类型的预订,然后必须在3个表中检查starts_at / ends_at,依此类推。