是否可以使用宏为所有Ecto模型创建默认的新功能?

时间:2019-01-21 21:32:58

标签: elixir phoenix-framework ecto

我希望我的所有模型都可以使用new便捷功能,该功能返回自定义更改集。

类似的东西:

def new(attrs) do
  changeset(%__MODULE__{}, attrs)
end

这样,当我需要变更集验证时,我可以致电:

Project.Model.new(%{param1: "param1"})

代替:

Project.Model.changeset(%Model{}, %{param1: "param1"})

我遇到的问题是当我实现以下宏时:

defmodule Project.Model do
  defmacro __using__(_) do
    quote do
      use Ecto.Schema
      import Ecto.Changeset
      def new(attrs) do
        changeset(%__MODULE__{}, attrs)
      end
    end
  end
end

...这是行不通的,因为Ecto的schema "model" do ... end需要在我的use Project.Model语句之前进行编译,否则我将得到一个错误,该错误基本上表明我的模块未定义结构。

我可以仅将宏限制为new函数,然后将其放在使用它的模块的中间,但这似乎令人困惑。

有什么想法吗?

根据要求,下面提供完整代码:

再一次,所有这一切的目的是使模型use Project.Model获得便利函数new,该函数接受属性并将其放入changeset内,以便它们将在插入数据库之前进行验证。

正如一些人提到的那样,我得到的错误是,schema必须先扩展,然后才能在宏中使用__MODULE__,因为尚未定义该结构。

项目/user.ex

defmodule Project.User do
  use Project.Model

  schema "users" do
    field :email, :string
    field :first_name, :string
    field :last_name, :string

    timestamps()
  end

  def changeset(user, attrs) do
    user
    |> cast(attrs, [:email, :first_name, :last_name])
    |> validate_required([:email, :first_name, :last_name])
  end
end

project / model.ex

defmodule Project.Model do
  defmacro __using__(_) do
    quote do
      use Ecto.Schema
      import Ecto.Changeset
      def new(attrs) do
        changeset(%__MODULE__{}, attrs)
      end
    end
  end
end

2 个答案:

答案 0 :(得分:1)

要在定义结构之前使用结构,可以尝试以下技巧:

  def new(attrs) do
    changeset(struct(__MODULE__), attrs)
  end

changeset是您应该为每个模型定义的(可能具有不同的形状),所以我不确定以一般方式在new中使用它是一个好主意。

答案 1 :(得分:1)

此代码的主要问题是AST扩展的宏顺序。我怀疑我理解为什么您手头有普通的旧商品Ecto.Changeset.change/2时为什么会尝试使用注入的#changeset。只要做:

defmodule Project.Model do
  defmacro __using__(_) do
    quote do
      use Ecto.Schema
      import Ecto.Changeset

      def new(attrs) do
        __MODULE__
        |> struct(%{})
        |> change(attrs)
      end
    end
  end
end

您应该已经准备就绪,因为Ecto.Changeset.change/2是正常功能,不会尝试扩展到AST。