将已有模型的Ecto模型作为关联插入

时间:2016-03-28 02:01:35

标签: elixir phoenix-framework elixir-framework

我有2个模型,entries

schema "entries" do
  belongs_to :exception, Proj.Exception
  field :application, :string
end

exceptions

schema "exceptions" do
  field :name, :string
end

迁移脚本:

def change do
  create table(:exceptions) do
    add :name, :string, null: false
  end

  create table(:entries) do
    add :exception_id, references(:exceptions), null: false
    add :application, :string, null: false
  end
end

我的目标是存储在另一个系统中发生的异常。我希望项目能够将每个异常存储在第二个表exception中(如果它们尚不存在),然后将应用程序名称和异常ID存储在第一个表entries中。 entries中有1000条记录,exceptions中有少数记录。

假设entry_params使用此JSON格式:

{
  exception: "NPE",
  application: "SomeApp"
}

应该创建条目的方法:

def create(conn, %{"entry" => entry_params}) do
  exception = Repo.get_by(Exception, name: entry_params["exception"]) || 
    Repo.insert!(%Exception{name: entry_params["exception"]})

  changeset =
    Entry.changeset(%Entry{}, entry_params)
    |> Ecto.Changeset.put_assoc(:exception, exception)

  Repo.insert!(changeset)
end

这将打印出来:

** (ArgumentError) unknown assoc `exception` in `put_assoc`

如果我将entries模型更改为使用has_one而不是belongs_to(我认为belongs_to"感觉"这里不好。一个条目不属于异常,它只是有一个例外)它抛出以下内容:

** (Postgrex.Error) ERROR (not_null_violation): null value in column "exception_id" violates not-null constraint

     table: entries
     column: exception_id

我想要的基本上首先创建一个Exception(如果它不存在),然后创建一个新的系统错误条目,并将先前生成的Exception作为关联放入条目中。

这里有什么问题?

1 个答案:

答案 0 :(得分:4)

  • 错字即可。 belongs_to :exception, Proj.Exception应为belongs_to :exceptions, Proj.Exception
  • 协会即可。基于问题中的数据模型,我认为put_assoc是错误的方法,因为在问题的数据模式中,异常 has_many 条目条目 belongs_to 例外Ecto.Changeset.put_assoc(entries_changeset, :exception, exception)应为Ecto.Changeset.put_assoc(exception_changeset, :entries, entries)

尝试解决方案:

entries架构:

schema "entries" do
  field :application, :string
  belongs_to :exceptions, Proj.Exception, on_replace: :nilify
end

exceptions架构:

schema "exceptions" do
  field :name, :string
  has_many :entry, Proj.Entry, on_delete: :delete_all, on_replace: :delete
end

迁移脚本:

def change do
  create table(:exceptions) do
    add :name, :string, null: false
  end

  create table(:entries) do
    add :application, :string, null: false
    add :exception_id, references(:exceptions)
  end
end

假设entry_params使用此JSON格式:

{
  exception: "NPE",
  application: "SomeApp"
}

创建或更新exceptions以及相关的entries

def create(conn, %{"entry" => entry_params}) do
  new_entry = Entry.changeset(%Entry{}, entry_params)

  changeset = 
  case Repo.get_by(Exception, name: entry_params["exception"]) do
    :nil -> 
      exception = %Exception{name: entry_params["exception"]} |> Repo.insert!
      Ecto.Changeset.build_assoc(exception, :entries, [new_entry])
    struct -> 
      changeset = Ecto.Changeset.change(struct) 
      data = Ecto.Changeset.preload(changeset, :entries) |> Map.get(:model) # Ecto 1.x
      # data = Ecto.Changeset.preload(changeset, :entries) |> Map.get(:data) # Ecto 2.0.x
      Ecto.Changeset.put_assoc(changeset, :entries, [new_entry | data.entries])
  end 

  Repo.insert!(changeset)
end