我是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_at
和starts_at
的插槽。如果有的话,我需要防止保存记录并向其中添加错误(类似于我们在throw :abort
和errors.add
中完成的Rails操作)。
有人可以阐明这一点吗? Ecto这样做的方式是什么?
最诚挚的问候
答案 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,依此类推。