Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Ecto's has_many and cast_assoc

Tags:

elixir

ecto

I'm new to Elixir and Ecto and I would need some help with Ecto's has_many and cast_assoc. Can't understand the basics, like how am I to create a new model with assoc one.

Here's my Has_Model:

defmodule Example.Has_Model do
  use Ecto.Schema
  import Ecto.Changeset
  alias Example.Repo
  alias Example.Has_Model

  schema "has_models" do
    has_many :belong_models, Example.Belong_Model
    field :name, string
    timestamps
  end

  def changeset(has_model, params \\ %{}) do
    has_model
    |> cast(params, [:name])
  end
end

and here's Belong_Model:

defmodule Example.Belong_Model do
  use Ecto.Schema
  import Ecto.Changeset
  alias Example.Repo
  alias Example.Belong_Model

  schema "belong_models" do
    belongs_to :has_model, Example.Has_Model
    field :name, string
    timestamps
  end

  def changeset(belong_model, params \\ %{}) do
    belong_model
    |> cast(params, [:name])
    |> cast_assoc(:has_model, required: true)
  end
end

Now, what I try to do is first to create a Has_Model

iex()> changeset = Example.Has_Model.changeset(%Has_Model, %{name: "I have"})
iex()> Example.Repo.insert(changeset)

This work ok.

Then I'd like to create a new Belong_Model and use that previously created Has_Model with it:

iex()> has = Example.Has_Model |> Example.Repo.get_by(name: "I have")
iex()> changeset = Example.Belong_Model.changeset(%Belong_Model, %{name: "I belong", belongs_to: has})

And this is where it fails:

** (Ecto.CastError) expected params to be a map, got: `%Example.Has_Model{__meta__: #Ecto.Schema.Metadata<:loaded, "has_models">, id: 1, inserted_at: #Ecto.DateTime<2016-10-04 19:39:38>, name: "I have", updated_at: #Ecto.DateTime<2016-10-04 19:39:38>, belong_models: #Ecto.Association.NotLoaded<association :belong_models is not loaded>}`
    (ecto) lib/ecto/changeset.ex:345: Ecto.Changeset.do_cast/4
    (example) lib/example/models/has_model.ex:15: Example.Has_Model.changeset/2
    (ecto) lib/ecto/changeset/relation.ex:99: Ecto.Changeset.Relation.do_cast/5
    (ecto) lib/ecto/changeset/relation.ex:235: Ecto.Changeset.Relation.single_change/5
    (ecto) lib/ecto/changeset.ex:571: Ecto.Changeset.cast_relation/4
    (example) lib/example/models/belong_model.ex:16: Example.Belong_Model.changeset/2

What's happening and what's the solution? I've tried to turn these parameters around the whole day but I just don't get it.

like image 673
Gulliver Avatar asked Nov 08 '22 08:11

Gulliver


1 Answers

I think what you are looking for is build_assoc/3

iex()> has = Example.Has_Model |> Example.Repo.get_by(name: "I have")
iex()> belong_assoc = Ecto.build_assoc(has, :belong_models, has_model_id: has.id, name: "I belong")
iex()> Repo.insert!(belong_assoc)

And cast_assoc/3 should be use like this:

iex()> changeset = Example.Belong_Model.changeset(%Belong_Model, %{name: "I belong", has_model: %{name: "I have"})
iex()> Repo.insert!(changeset)

Note:

The parameters for the given association will be retrieved from changeset.params and the changeset function in the association module will be invoked

like image 191
TheAnh Avatar answered Nov 15 '22 10:11

TheAnh