我有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(我认为这里的“感到”很糟糕。条目不属于异常,它只是一个异常),它将引发以下情况:
** (Postgrex.Error) ERROR (not_null_violation): null value in column "exception_id" violates not-null constraint

     table: entries
     column: exception_id

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

这是怎么了

最佳答案

  • 错字belongs_to :exception, Proj.Exception应该是belongs_to :exceptions, Proj.Exception
  • 关联。基于问题中的数据模型,我认为put_assoc是错误的处理方法,因为在问题中的数据模式中,异常has_many项和entry_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
    

    关于phoenix-framework - 将Ecto模型与已经存在的模型作为关联插入,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36254866/

    10-12 19:48