Elixir: 1.2.4
Postgres: 9.5.2
ecto 2.0.0-rc.5 (Hex package) (mix)
phoenix_ecto 3.0.0-rc.0 (Hex package) (mix)
postgrex 0.11.1 (Hex package) (mix)
OSX 10.11.3
defmodule Demo.Club do
use Demo.Web, :model
schema "clubs" do
field :name, :string
has_many :members, Demo.Member
timestamps
end
end
defmodule Demo.Member do
use Demo.Web, :model
schema "members" do
field :name, :string
belongs_to :club, Demo.Club
timestamps
end
end
test "can associate existing member with existing club" do
member = Repo.insert!(%Member{name: "Dave"}) |> Repo.preload(:club)
club = Repo.insert!(%Club{name: "Wentworth"})
changeset = member
|> Changeset.change
|> Changeset.put_assoc(:club, club)
assert changeset.valid?
member = Repo.update! changeset
assert member
end
test "can change association for existing member with existing club" do
member = Repo.insert!(%Member{name: "Dave"}) |> Repo.preload(:club)
club = Repo.insert!(%Club{name: "Wentworth Club"})
club2 = Repo.insert!(%Club{name: "Palm Club"})
changeset = member
|> Changeset.change
|> Changeset.put_assoc(:club, club)
assert changeset.valid?
member = Repo.update! changeset
assert member
changeset = member
|> Changeset.change
|> Changeset.put_assoc(:club, club2)
assert changeset.valid?
member = Repo.update! changeset
assert member
end
test "can change association for existing member with existing club using struct reference for member" do
member = Repo.insert!(%Member{name: "Dave"}) |> Repo.preload(:club)
club = Repo.insert!(%Club{name: "Wentworth Club"})
club2 = Repo.insert!(%Club{name: "Palm Club"})
changeset = member
|> Changeset.change
|> Changeset.put_assoc(:club, club)
assert changeset.valid?
member = Repo.update! changeset
assert member
changeset = %Member{id: member.id}
|> Changeset.change
|> Changeset.put_assoc(:club, club2)
assert changeset.valid?
member = Repo.update! changeset
assert member
end
### The error that is returned for that second test case is:
** (RuntimeError) you are attempting to change relation :club of Demo.Member, but there is missing data. If you are attempting to update an existing entry, please make sure you include the entry primary key (ID) alongside the data. If you have a relationship with many children, at least the same N children must be given on update. By default it is not possible to orphan embed nor associated records, attempting to do so results in this error message. It is possible to change this behaviour by setting `:on_replace` when defining the relation. See `Ecto.Changeset`'s section on related data for more info.
Thank you @carusso!
It is all correct. The reason the third does not fail is because you don't have any ID in your structure, so Ecto does not know data is being replaced. The solution is to set :on_replace when you declare the belongs_to and specify what you want to happen. :nilify makes the most sense if you simply want to replace one club by the other.
Not sure what you mean by "don't have any ID in your structure", since I did have an id on Member:
%Member{id: member.id}
Maybe my confusion was on the naming of that :on_replace option.
What exactly is being nilified? Effectively, all I want is for member.club_id to be "replaced".
I meant FK instead ID. What is being nullified is the FK as well, which is
then immediately set to the ID of the new club.
On Sunday, May 15, 2016, Chris R [email protected] wrote:
Not sure what you mean by "don't have any ID in your structure", since I
did have an id on Member:
%Member{id: member.id}Maybe my confusion was on the naming of that :on_replace option.
What exactly is being nilified? Effectively, all I want is for
member.club_id to be "replaced".—
You are receiving this because you modified the open/close state.
Reply to this email directly or view it on GitHub
https://github.com/elixir-lang/ecto/issues/1432#issuecomment-219279805
_José Valimwww.plataformatec.com.br
http://www.plataformatec.com.br/Founder and Director of R&D_
Okay, thank you. I would recommend renaming the :nilify option to something like :force, :accept, or :allow. :nilify implies an end state of nil but doesn't really tell me that the :on_replace is allowing the fk relation to simply be replaced. When I read the documentation looking for replacing the fk, I saw ":nilify" and thought, "that can't be it."
In general, the documentation could use a lot more explanation around what is occurring in operations involving associations. When I used similar concepts in Rails, I found it much easier to comprehend what was happening at an abstract level - even though I know that ActiveRecord was doing a bunch of magic behind the scenes that you want to avoid in Phoenix.
The flip side of not having the framework do all the magic is that the developer needs a better understanding of the internals.
I'll think about it and experiment so I can understand it better and possibly contribute something to the docs.
Thanks again.
Awesome, PRs for docs are always welcome!
On Sunday, May 15, 2016, Chris R [email protected] wrote:
Okay, thank you. I would recommend renaming the :nilify option to
something like :force, :accept, or :allow. :nilify implies an end state of
nil but doesn't really tell me that the :on_replace is allowing the fk
relation to simply be replaced. When I read the documentation looking for
replacing the fk, I saw ":nilify" and thought, "that can't be it."In general, the documentation could use a lot more explanation around what
is occurring in operations involving associations. When I used similar
concepts in Rails, I found it much easier to comprehend what was happening
at an abstract level - even though I know that ActiveRecord was doing a
bunch of magic behind the scenes that you want to avoid in Phoenix.The flip side of not having the framework do all the magic is that the
user needs a better understanding of the internals.I'll think about it and experiment so I can understand it better and
possibly contribute something to the docs.Thanks again.
—
You are receiving this because you modified the open/close state.
Reply to this email directly or view it on GitHub
https://github.com/elixir-lang/ecto/issues/1432#issuecomment-219289887
_José Valimwww.plataformatec.com.br
http://www.plataformatec.com.br/Founder and Director of R&D_
Most helpful comment
Okay, thank you. I would recommend renaming the :nilify option to something like :force, :accept, or :allow. :nilify implies an end state of nil but doesn't really tell me that the :on_replace is allowing the fk relation to simply be replaced. When I read the documentation looking for replacing the fk, I saw ":nilify" and thought, "that can't be it."
In general, the documentation could use a lot more explanation around what is occurring in operations involving associations. When I used similar concepts in Rails, I found it much easier to comprehend what was happening at an abstract level - even though I know that ActiveRecord was doing a bunch of magic behind the scenes that you want to avoid in Phoenix.
The flip side of not having the framework do all the magic is that the developer needs a better understanding of the internals.
I'll think about it and experiment so I can understand it better and possibly contribute something to the docs.
Thanks again.