代码之家  ›  专栏  ›  技术社区  ›  sambecker

是否可以使用宏为所有ECO模型创建默认的新函数?

  •  0
  • sambecker  · 技术社区  · 6 年前

    我希望我所有的模特都能 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
    

    …它不起作用,因为埃科 schema "model" do ... end 需要在我的 use Project.Model 语句,否则我将得到一个错误,基本上说我的模块没有定义结构。

    我可以简单地将宏限制为 新的 函数并将其放在使用它的模块的中间,但这看起来很混乱。

    有什么想法吗?

    根据要求,完整代码如下:

    再一次,所有这些的目的,是要有模型 使用项目.模型 获得便利功能 新的 接受属性并将其放入 changeset 以便在插入数据库之前对它们进行验证。

    正如有些人所说,我所犯的错误是 schema 在我使用之前必须扩展 __MODULE__ 在我的宏中,因为尚未定义结构。

    项目/用户

    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
    

    项目/型号.ex

    defmodule项目.model do
    使用do定义宏
    报价做
    使用ECT.模式
    导入外部变更集
    新定义(ATTR)DO
    变更集(%模块,属性)
    结束
    结束
    结束
    结束
    
    2 回复  |  直到 6 年前
        1
  •  1
  •   sambecker    6 年前

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

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

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

        2
  •  1
  •   Aleksei Matiushkin    6 年前

    此代码的主要问题是宏到AST扩展的顺序。我不明白你为什么要用注射的 #changeset 当你有一个普通的老好人 Ecto.Changeset.change/2 在手边。只做:

    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的正常函数。