Phoenix Ecto: foreign key not inserted

I am inserting a model A that contains a foreign key for another model B.

defmodule MyApp.ModelA do use MyApp.Web, :model schema "model_a" do field :type, :string, null: false field :data, :string, null: false belongs_to :model_b, MyApp.ModelB timestamps() end @required_fields ~w(type data) @optional_fields ~w() @doc """ Builds a changeset based on the `struct` and `params`. """ def changeset(struct, params \\ %{}) do struct |> cast(params, @required_fields, @optional_fields) |> assoc_constraint(:model_b) end end 

and model B:

 defmodule MyApp.ModelB do use MyApp.Web, :model schema "model_b" do field :username, :string field :pass, :string has_many :model_a, MyApp.ModelA timestamps() end @required_fields ~w(username pass) @optional_fields ~w() @doc """ Builds a changeset based on the `struct` and `params`. """ def changeset(struct, params \\ %{}) do struct |> cast(params, @required_fields, @optional_fields) |> cast_assoc(:model_a) |> validate_required([]) end end 

Model B exists since I can get it through Repo.all (ModelB).

Model Changing the parameter set has been tested successfully, and I can see the value of model_b_id when I print the model A changeet struct.

But when inserted, the link is not inserted. Although I see this when printing the model A change set, this field is completely absent in the MySQL log, it is not in the INSERT query.

I played a little, and if I make this reference field not be null in the MySQL table, then I get {"does not exist", []} for this foreign key field when pasted as Repo.insert (...) answer although model B exists in the database.

+5
source share
3 answers

I don't think Ecto is pulling an identifier field out of the structure for you - I was doing something like this:

 defmodule MyApp.ModelA do use MyApp.Web, :model schema "model_a" do field :type, :string, null: false field :data, :string, null: false belongs_to :model_b, MyApp.ModelB timestamps() end @required_fields ~w(type data model_b_id) @optional_fields ~w() @doc """ Method head to define what coming. """  def changeset(model, params \\ :empty) @doc """ Catches the case where you're passing in a model_b struct instead of an integer. Just makes a recursive call with the correct type. """  def changeset(model, %{model_b_id: nil, agency: %MyApp.ModelB{} = model_b} = params) do    changeset(model, %{params | model_b_id: model_b.id})  end @doc """ I normally use assoc_constraint on the table index itself like so """ def changeset(struct, params \\ %{}) do struct |> cast(params, @required_fields, @optional_fields) |> assoc_constraint(:model_b, name: :model_as_model_b_id_fkey) end end 
0
source

I was extremely interested in answering this question.

I lost> 7 hours to the exact condition "I KNOW this line was created, WHY IT WHY is it a set of changes that does not allow me to store the link?"

I thought I left an offer for someone with the same frustration to check links to database migration .

I was sure that I checked them three times, but there it was clear how the day after a good night's sleep. I set links to the wrong table.

Hope this saves you a little time.

0
source

What worked for me was the following:

 model_b = Repo.get(MyApp.ModelB, 2) model_a_changeset = Ecto.build_assoc(model_b, :model_as, type: "Model type", data: "Model data" ) Repo.insert! model_a_changeset 

(Number 2 is an example of model_b id, you need to figure out how to get the correct parent id)

Sometimes you need to explicitly specify a foreign key in a change set as follows:

 model_a_changeset = Ecto.build_assoc(model_b, :model_as, type: "Model type", data: "Model data", model_b_id: model_b.id ) 
0
source

All Articles