我是Elixir和凤凰城的新手(不到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这样做的方式?
最好的祝福
*编辑:使用单独的更改集添加示例以进行创建和更新
您可以在变更集验证链中添加自定义验证功能,并在其中执行数据库查询。
没有运行此代码,但这样的事情应该工作
# 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,依此类推。