Insert an Ecto model with an existing model as an association

I have 2 models, entries :

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

And exceptions :

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

Migration script:

 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 

My goal is to store exceptions that occur on another system. I want the project to be able to store each exception in the second exception table if they do not already exist, and then store the application name and exception identifier in the first entries table. In entries will be 1000 units of entries and several in exceptions .

Assuming entry_params uses this JSON format:

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

method that should create entries:

 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 

This will print:

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

If I modify the entries model to use has_one instead of belongs_to (and I think that belonging to โ€œfeelsโ€ is bad here. The entry is not an exception, it just has an exception), it throws the following:

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

Basically I want to create an exception (if it does not exist), and then create a new system error record and put the previously excluded exception into the record as an association.

What is wrong here?

+7
elixir phoenix-framework elixir-framework
source share
1 answer
  • typo . belongs_to :exception, Proj.Exception must be belongs_to :exceptions, Proj.Exception
  • Association . Based on the data model in the question, I think put_assoc is the wrong way, because in the data schema in question, the has_many exception is thrown and entry belongs to the exceptions. Ecto.Changeset.put_assoc(entries_changeset, :exception, exception) must be Ecto.Changeset.put_assoc(exception_changeset, :entries, entries)

Attempted Solution:

entries scheme:

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

exceptions scheme:

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

migration script:

 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 

Assuming entry_params uses this JSON format:

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

create or update exceptions and related 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 
+4
source share

All Articles