Ecto,Phoenix:如何使用 embeds_many 声明更新模型?

Mar*_*iro 3 postgresql elixir ecto phoenix-framework

我有两个模型OwnerProperty,其中 的架构Owner有一个embeds_many声明,如下所示:

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

  alias MyApp.Property

  schema "owners" do
    field name, :string
    embeds_many :properties, Property
    timestamps()
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.
  """
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [])
    |> validate_required([])
  end
end
Run Code Online (Sandbox Code Playgroud)

和这个:

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

  embedded_schema do
    field :name, :string
    field :value, :float, default: 0
  end

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:name, :value])
    |> validate_required([:name])
  end
end
Run Code Online (Sandbox Code Playgroud)

我正在使用的迁移是:

defmodule MyApp.Repo.Migrations.CreateOwner do
  use Ecto.Migration

  def down do
    drop table(:owners)
  end

  def change do
    drop_if_exists table(:owners)
    create table(:owners) do
      add :name, :string
      add :properties, :map
      timestamps()
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

可能的种子是:

alias MyApp.{Repo, Owner, Property}

Repo.insert!(%Owner{
  name: "John Doe",
  properties: [
    %Property{
      name: "Property A"
    },
    %Property{
      name: "Property B",
      value: 100000
    },
    %Property{
      name: "Property C",
      value: 200000
    }
  ]
})
Run Code Online (Sandbox Code Playgroud)

最后,我的问题是:如何将John DoeProperty C值从 更新200000300000?如果John Doe购买Property D

%Property{
  name: "Property D"
  value: 400000
}
Run Code Online (Sandbox Code Playgroud)

我如何将其添加到他的properties数据库中?(我正在使用Postgres)。

Dog*_*ert 5

最简单的方法是获取记录、更新properties 列表并保存更改:

owner = Repo.get!(Owner, 1)
properties = owner.properties

# update all properties with name "Property C"'s value to 400000
properties = for %Property{name: name} = property <- properties do
  if name == "Property C" do
    %{property | value: 400000}
  else
    property
  end
end

# add a property to the start
properties = [%Property{name: "Property D", value: 400000} | properties]

# or to the end
# properties = properties ++ [%Property{name: "Property D", value: 400000}]

# update
Owner.changeset(owner, %{properties: properties})
|> Repo.update!
Run Code Online (Sandbox Code Playgroud)

您可以使用 PostgreSQL 提供的 JSON 函数执行一些操作(至少插入属性)fragment,但我不认为您可以使用它们搜索和有条件更新数组的项目。