From 8b258e4f205a337d954fb81d7bec127e545e9b31 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Fri, 30 Aug 2024 06:43:59 -0400 Subject: [PATCH] Allow associations to be cast/put inside of embedded schema changesets (#4504) --- lib/ecto/changeset.ex | 22 ++-------------------- lib/ecto/schema.ex | 9 ++------- test/ecto/changeset/belongs_to_test.exs | 25 ------------------------- 3 files changed, 4 insertions(+), 52 deletions(-) diff --git a/lib/ecto/changeset.ex b/lib/ecto/changeset.ex index 1e284ee5d2..4f671ddbd8 100644 --- a/lib/ecto/changeset.ex +++ b/lib/ecto/changeset.ex @@ -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 @@ -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 diff --git a/lib/ecto/schema.ex b/lib/ecto/schema.ex index 195cae83c7..77ee07fc57 100644 --- a/lib/ecto/schema.ex +++ b/lib/ecto/schema.ex @@ -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 diff --git a/test/ecto/changeset/belongs_to_test.exs b/test/ecto/changeset/belongs_to_test.exs index 6aeb11cb13..f62ff36b46 100644 --- a/test/ecto/changeset/belongs_to_test.exs +++ b/test/ecto/changeset/belongs_to_test.exs @@ -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()) @@ -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 @@ -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 =