Phoenix 框架和验证嵌入

Phoenix frameworks and validating embeds

鉴于以下代码工作正常:

image_1 = %Image{naturalHeight: "100", naturalWidth: 100}

diffbot_objects = [
  %DiffbotObject{
    availability: true,
    images: [
      image_1
    ]
  }
]

changeset = Ecto.Changeset.change(product)
changeset = Ecto.Changeset.put_embed(changeset, :diffbot_objects, diffbot_objects)

如何确保字段在图像模型上得到验证?我可以使用图像模型上的变更集方法生成一个变更集(见下文),但我无法使用嵌套变更集插入数据,它似乎必须是 e 结构。

我的图片模型:

defmodule Shopshare.Product.DiffbotObject.Image do
  use Shopshare.Web, :model

  embedded_schema do
    field :naturalHeight, :integer
    field :naturalWidth, :integer
  end

  @required_fields ~w(naturalHeight, naturalWidth)
  @optional_fields ~w()

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

我看到您正在使用 put_embed,但我没有看到产品的架构。我不知道这是否是真正的问题。但我尝试了一些有效的代码。

我用博客和 Post 模型创建了一个新应用。我使用了生成模型:

mix phoenix.gen.model Blog blogs name:string
mix phoenix.gen.model Post posts title:string body:text blog_id:references:blogs

我正在对必填字段使用简单的验证。让我们更深入地了解博客模型:

    defmodule MyApp.Blog do
      use MyApp.Web, :model

      schema "blogs" do
        field :name, :string
        has_many :posts, MyApp.Post

        timestamps
      end

      @required_fields ~w(name)
      @optional_fields ~w()

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

我手动创建 has_many 与帖子的关联(它不是使用我的生成模型创建的)。现在,我们有了 Post 模型:

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

      schema "posts" do
        field :title, :string
        field :body, :string
        belongs_to :blog, MyApp.Blog

        timestamps
      end

      @required_fields ~w(title body)
      @optional_fields ~w()

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

现在可以玩IEx了(iex -S mix):

    iex(1)> blog_changeset = MyApp.Blog.changeset(%MyApp.Blog{}, %{name: "blog name"})
    %Ecto.Changeset{...}

    iex(2)> blog_changeset.valid?
    true

    iex(3)> invalid_post_changeset = MyApp.Post.changeset(%MyApp.Post{}, %{})
    %Ecto.Changeset{...}

    iex(4)> blog_changeset = Ecto.Changeset.put_assoc(blog_changeset, :posts, [invalid_post_changeset])
    %Ecto.Changeset{action: nil,
    changes: %{name: "blog name",
      posts: [%Ecto.Changeset{action: :insert,
        changes: ..., constraints: [],
        errors: [title: "can't be blank", body: "can't be blank"], filters: %{},
        ...]}, ...,
    model: %MyApp.Blog{...}, optional: [], opts: [], params: %{"name" => "blog name"},
    prepare: [], repo: nil, required: [:name],
    types: %{...},
    valid?: false, validations: []}

    iex(5)> blog_changeset.valid?
    false

我抑制了一些输出以关注错误。 Changeset 像树一样工作。因此,您可以拥有父变更集和子变更集。与您的代码不同的是,我使用的是 put_assoc (https://hexdocs.pm/ecto/Ecto.Changeset.html#put_assoc/4)(也许关系也是如此,但我没有看到您的架构)。

put_assoc 的预期行为:

If the association has no changes, it will be skipped. If the association is invalid, the changeset will be marked as invalid. If the given value is not an association, it will raise.

希望对您有所帮助。