【问题标题】:Query records before insertion with Ecto (similar to an AR callback)使用 Ecto 在插入前查询记录(类似于 AR 回调)
【发布时间】:2019-03-08 02:16:13
【问题描述】:

我是 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 匹配。如果有,我需要阻止记录被保存并向其添加错误(类似于我们在 Rails 中使用throw :aborterrors.add 完成的操作)。

有人可以解释一下吗?这样做的 Ecto 方法是什么?

最好的问候

【问题讨论】:

    标签: callback elixir phoenix-framework ecto


    【解决方案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 等等。

    【讨论】:

    • Ecto 怎么知道这应该只在创建时运行,而不是在后续更新时运行?
    • 你可以有多个变更集函数。所以你可以有一个通用的changeset 函数,但你也可以有create_changesetupdate_changeset 或任何你想要的数字。请记住,这些只是函数,它们甚至可以互相调用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-18
    • 1970-01-01
    • 2012-09-10
    • 1970-01-01
    • 1970-01-01
    • 2014-12-04
    相关资源
    最近更新 更多