Elixir Ecto仅在create上添加计算值

Ell*_*son 3 elixir ecto phoenix-framework

在create/insert上添加计算值的最佳实践方法是什么?我应该为创建和更新创建唯一的变更集吗?

比方说,我有一个博客文章模型,我想创建一个标题slug值并存储它.这有点做作,但出于某种原因说我只想在创建而不是更新时设置它.我应该做以下事情吗?

defmodule MyBlog.Post do
  use MyBlog.Web, :model

  schema "posts" do
    field :title, :string
    field :title_slug, :string
    field :content, :text

    timestamps
  end

  @required_fields ~w(
    title 
    content
  )

  @optional_fields ~w()

  def create_changeset(model, params \\ :empty) do
    changeset(model, params)
    |> generate_title_slug
  end

  defp changeset(model, params \\ :empty) do
    model
    |> cast(params, @required_fields, @optional_fields)
  end

  defp generate_title_slug(changeset) do
    put_change(changeset, :title_slug, __some_slug_generation_code__)
  end

  def update_changeset(model, params \\ :empty) do
    changeset(model, params)
  end
end
Run Code Online (Sandbox Code Playgroud)

mic*_*ala 6

我强烈反对回调 - 它们难以测试,引入全局状态,模糊不清,难以推理.这也违背了Elixir的核心原则之一:"明确胜过隐性".

Ecto的核心团队甚至正在考虑摆脱回调,或改变名称并减少暴露.在没有其他可能的情况下,使用回调应该是最后的选择.

为了展示回调的问题之一,让我们想象一下你确实使用回调来解决这个问题的场景.现在,您正在设计一个管理界面,您不希望出现此行为.你是如何解决这个问题的?你开始走下禁用回调的兔子洞,在异常时引入异常,并且有一个难以理解的多分支条件逻辑.但这一起解决了一个错误的问题!

对于Ecto的架构,不同的变更集方法非常精细且非常自然.通过这种方式,您可以对不同的操作进行不同的验证,而且不会是全局的 让我们考虑如何在我之前展示的场景中解决问题.它非常简单 - 您创建另一个变更集功能!

我见过几次的解决方案是将changeset函数更改为在第一个类型中采用三个参数和模式匹配,例如:

def changeset(action, model, params \\ :empty)

def changeset(:create, model, params)
  # return create changeset
end

def changeset(:update, model, params)
  # return update changeset
end
Run Code Online (Sandbox Code Playgroud)

我不确定哪个更好 - 一个函数中的多个函数或模式匹配.这主要是偏好问题.

  • 就变更集的多个功能或模式匹配而言,使用不同功能的一个原因是它们可以单独记录.这允许您解释为什么在更新而不是创建时存在不同的变更集. (2认同)