Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

get_field/3 and fetch_field/2 for relations return models #876

Merged
merged 1 commit into from
Aug 14, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions lib/ecto/changeset.ex
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,10 @@ defmodule Ecto.Changeset do
then falls back on the model, finally returning `:error` if
no value is available.

For relations this functions will return the models with changes applied,
as if they were taken from model.
To retrieve raw changesets, please use `fetch_change/2`.

## Examples

iex> post = %Post{title: "Foo", body: "Bar baz bong"}
Expand All @@ -568,9 +572,10 @@ defmodule Ecto.Changeset do

"""
@spec fetch_field(t, atom) :: {:changes, term} | {:model, term} | :error
def fetch_field(%{changes: changes, model: model} = _changeset, key) do
def fetch_field(%Changeset{changes: changes, model: model, types: types}, key) do
case Map.fetch(changes, key) do
{:ok, value} -> {:changes, value}
{:ok, value} ->
{:changes, change_as_field(types, key, value)}
:error ->
case Map.fetch(model, key) do
{:ok, value} -> {:model, value}
Expand All @@ -587,6 +592,10 @@ defmodule Ecto.Changeset do
then falls back on the model, finally returning `default` if
no value is available.

For relations this functions will return the models with changes applied,
as if they were taken from model.
To retrieve raw changesets, please use `get_change/3`.

iex> post = %Post{title: "A title", body: "My body is a cage"}
iex> changeset = change(post, %{title: "A new title"})
iex> get_field(changeset, :title)
Expand All @@ -596,9 +605,10 @@ defmodule Ecto.Changeset do

"""
@spec get_field(t, atom, term) :: term
def get_field(%Changeset{changes: changes, model: model} = _changeset, key, default \\ nil) do
def get_field(%Changeset{changes: changes, model: model, types: types}, key, default \\ nil) do
case Map.fetch(changes, key) do
{:ok, value} -> value
{:ok, value} ->
change_as_field(types, key, value)
:error ->
case Map.fetch(model, key) do
{:ok, value} -> value
Expand All @@ -607,6 +617,15 @@ defmodule Ecto.Changeset do
end
end

defp change_as_field(types, key, value) do
case Map.get(types, key) do
{tag, relation} when tag in @relations ->
Relation.apply_changes(relation, value)
_other ->
value
end
end

@doc """
Fetches a change from the given changeset.

Expand Down
19 changes: 19 additions & 0 deletions test/ecto/association_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,25 @@ defmodule Ecto.AssociationTest do
assert %Ecto.Changeset{} = changeset.changes.profile
end

test "get_field/3, fetch_field/2 with assocs" do
profile_changeset = Changeset.change(%Profile{}, name: "michal")
profile = Changeset.apply_changes(profile_changeset)

changeset = Changeset.change(%Author{}, profile: profile_changeset)
assert Changeset.get_field(changeset, :profile) == profile
assert Changeset.fetch_field(changeset, :profile) == {:changes, profile}

changeset = Changeset.change(%Author{profile: profile})
assert Changeset.get_field(changeset, :profile) == profile
assert Changeset.fetch_field(changeset, :profile) == {:model, profile}

post = %Post{id: 1}
post_changeset = %{Changeset.change(post) | action: :delete}
changeset = Changeset.change(%Author{posts: [post]}, posts: [post_changeset])
assert Changeset.get_field(changeset, :posts) == []
assert Changeset.fetch_field(changeset, :posts) == {:changes, []}
end

test "on_replace: :nilify" do
# one case is handled inside repo
post = %Post{id: 1, summary_id: 5}
Expand Down
19 changes: 19 additions & 0 deletions test/ecto/embedded_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,25 @@ defmodule Ecto.EmbeddedTest do
assert %Ecto.Changeset{} = changeset.changes.profile
end

test "get_field/3, fetch_field/2 with embeds" do
profile_changeset = Changeset.change(%Profile{}, name: "michal")
profile = Changeset.apply_changes(profile_changeset)

changeset = Changeset.change(%Author{}, profile: profile_changeset)
assert Changeset.get_field(changeset, :profile) == profile
assert Changeset.fetch_field(changeset, :profile) == {:changes, profile}

changeset = Changeset.change(%Author{profile: profile})
assert Changeset.get_field(changeset, :profile) == profile
assert Changeset.fetch_field(changeset, :profile) == {:model, profile}

post = %Post{id: 1}
post_changeset = %{Changeset.change(post) | action: :delete}
changeset = Changeset.change(%Author{posts: [post]}, posts: [post_changeset])
assert Changeset.get_field(changeset, :posts) == []
assert Changeset.fetch_field(changeset, :posts) == {:changes, []}
end

test "empty" do
assert Relation.empty(%Embedded{cardinality: :one}) == nil
assert Relation.empty(%Embedded{cardinality: :many}) == []
Expand Down