Skip to content

Commit

Permalink
Allow associations to be cast/put inside of embedded schema changesets (
Browse files Browse the repository at this point in the history
  • Loading branch information
greg-rychlewski authored Aug 30, 2024
1 parent 02e6918 commit 8b258e4
Show file tree
Hide file tree
Showing 3 changed files with 4 additions and 52 deletions.
22 changes: 2 additions & 20 deletions lib/ecto/changeset.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1197,16 +1197,7 @@ defmodule Ecto.Changeset do
"""
@spec cast_assoc(t, atom, Keyword.t()) :: t
def cast_assoc(changeset, name, opts \\ [])

def cast_assoc(%Changeset{data: %{} = data}, _name, _opts)
when not is_map_key(data, :__meta__) do
raise ArgumentError,
"cast_assoc/3 cannot be used to cast associations into embedded schemas or schemaless changesets. " <>
"Please modify the association independently."
end

def cast_assoc(changeset, name, opts) when is_atom(name) do
def cast_assoc(changeset, name, opts \\ []) when is_atom(name) do
cast_relation(:assoc, changeset, name, opts)
end

Expand Down Expand Up @@ -2104,16 +2095,7 @@ defmodule Ecto.Changeset do
"""
@spec put_assoc(t, atom, term, Keyword.t()) :: t
def put_assoc(changeset, name, value, opts \\ [])

def put_assoc(%Changeset{data: %{} = data}, _name, _value, _opts)
when not is_map_key(data, :__meta__) do
raise ArgumentError,
"put_assoc/4 cannot be used to put associations into embedded schemas or schemaless changesets. " <>
"Please modify the association independently."
end

def put_assoc(%Changeset{} = changeset, name, value, opts) do
def put_assoc(%Changeset{} = changeset, name, value, opts \\ []) do
put_relation(:assoc, changeset, name, value, opts)
end

Expand Down
9 changes: 2 additions & 7 deletions lib/ecto/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -549,13 +549,8 @@ defmodule Ecto.Schema do
`@primary_key` attribute.
`belongs_to/3` associations may be defined inside of
embedded schemas. However, they are essentially read-only.
This means you may preload the associations but you may
not modify them by using `Ecto.Changeset.cast_assoc/3`
or `Ecto.Changeset.put_assoc/4`. If you would like to
modify the associations of an embedded schema, you must
change them independently. Associations nested inside of
embedded schemas will also not be persisted to the database
embedded schemas. However, any association nested inside
of an embedded schema won't be persisted to the database
when calling `c:Ecto.Repo.insert/2` or `c:Ecto.Repo.update/2`.
"""
defmacro embedded_schema(do: block) do
Expand Down
25 changes: 0 additions & 25 deletions test/ecto/changeset/belongs_to_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,6 @@ defmodule Ecto.Changeset.BelongsToTest do
end
end

defmodule Embed do
use Ecto.Schema

embedded_schema do
belongs_to :profile, Profile
end
end

defp cast(schema, params, assoc, opts \\ []) do
schema
|> Changeset.cast(params, ~w())
Expand Down Expand Up @@ -328,14 +320,6 @@ defmodule Ecto.Changeset.BelongsToTest do
assert changeset.valid?
end

test "cast belongs_to from embedded schema" do
msg = ~r"cast_assoc/3 cannot be used to cast associations into embedded schemas"

assert_raise ArgumentError, msg, fn ->
cast(%Embed{}, %{"profile" => %{"name" => "michal"}}, :profile)
end
end

## Change

test "change belongs_to" do
Expand Down Expand Up @@ -503,15 +487,6 @@ defmodule Ecto.Changeset.BelongsToTest do
refute Map.has_key?(changeset.changes, :profile)
end

test "put_assoc/4 from embedded schema" do
msg = ~r"put_assoc/4 cannot be used to put associations into embedded schema"
base_changeset = Changeset.change(%Embed{})

assert_raise ArgumentError, msg, fn ->
Changeset.put_assoc(base_changeset, :profile, %{name: "michal"})
end
end

test "put_assoc/4 with empty" do
# On unloaded
changeset =
Expand Down

0 comments on commit 8b258e4

Please sign in to comment.