Packages
ecto
0.8.1
3.14.1
3.14.0
3.13.6
3.13.5
3.13.4
3.13.3
3.13.2
3.13.1
3.13.0
3.12.6
3.12.5
3.12.4
3.12.3
3.12.2
3.12.1
3.12.0
3.11.2
3.11.1
3.11.0
3.10.3
3.10.2
3.10.1
3.10.0
3.9.6
3.9.5
3.9.4
3.9.3
3.9.2
3.9.1
3.9.0
3.8.4
3.8.3
3.8.2
3.8.1
3.8.0
3.7.2
3.7.1
3.7.0
3.6.2
3.6.1
3.6.0
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.5.0-rc.1
3.5.0-rc.0
3.4.6
3.4.5
3.4.4
3.4.3
3.4.2
3.4.1
3.4.0
3.3.4
3.3.3
3.3.2
3.3.1
3.3.0
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.1
3.0.0-rc.0
2.2.12
2.2.11
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.2.0-rc.1
2.2.0-rc.0
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.1.0-rc.5
2.1.0-rc.4
2.1.0-rc.3
2.1.0-rc.2
2.1.0-rc.1
2.1.0-rc.0
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
2.0.0-beta.2
2.0.0-beta.1
2.0.0-beta.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.16.0
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.1
0.13.0
0.12.1
0.12.0
0.12.0-rc
0.11.3
0.11.2
0.11.1
0.11.0
0.10.3
0.10.2
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.0
0.5.1
0.5.0
0.4.0
0.3.0
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.0
A toolkit for data mapping and language integrated query for Elixir
Current section
Files
Jump to
Current section
Files
lib/ecto/repo/model.ex
defmodule Ecto.Repo.Model do
# The module invoked by user defined repos
# for model related functionality.
@moduledoc false
alias Ecto.Query.Planner
alias Ecto.Model.Callbacks
@doc """
Implementation for `Ecto.Repo.insert/2`.
"""
def insert(repo, adapter, %Ecto.Changeset{} = changeset, opts) when is_list(opts) do
struct = struct_from_changeset!(changeset)
model = struct.__struct__
fields = model.__schema__(:fields)
source = model.__schema__(:source)
return = model.__schema__(:read_after_writes)
# On insert, we always merge the whole struct into the
# changeset as changes, except the primary key if it is nil.
changeset = %{changeset | repo: repo}
changeset = merge_into_changeset(model, struct, fields, changeset)
with_transactions_if_callbacks repo, adapter, model, opts,
~w(before_insert after_insert)a, fn ->
changeset = Callbacks.__apply__(model, :before_insert, changeset)
changes = validate_changes(:insert, model, fields, changeset)
{:ok, values} = adapter.insert(repo, source, changes, return, opts)
changeset = load_into_changeset(changeset, model, return, values)
Callbacks.__apply__(model, :after_insert, changeset).model
end
end
def insert(repo, adapter, %{__struct__: _} = struct, opts) do
insert(repo, adapter, %Ecto.Changeset{model: struct, valid?: true}, opts)
end
@doc """
Implementation for `Ecto.Repo.update/2`.
"""
def update(repo, adapter, %Ecto.Changeset{} = changeset, opts) when is_list(opts) do
struct = struct_from_changeset!(changeset)
model = struct.__struct__
fields = model.__schema__(:fields)
source = model.__schema__(:source)
return = model.__schema__(:read_after_writes)
# Differently from insert, update does not copy the struct
# fields into the changeset. All changes must be in the
# changeset before hand.
changeset = %{changeset | repo: repo}
with_transactions_if_callbacks repo, adapter, model, opts,
~w(before_update after_update)a, fn ->
changeset = Callbacks.__apply__(model, :before_update, changeset)
changes = validate_changes(:update, model, fields, changeset)
pk_filter = Planner.fields(:update, model, pk_filter(model, struct))
if changes == [] do
changes = pk_filter
end
{:ok, values} = adapter.update(repo, source, pk_filter, changes, return, opts)
changeset = load_into_changeset(changeset, model, return, values)
Callbacks.__apply__(model, :after_update, changeset).model
end
end
def update(repo, adapter, %{__struct__: model} = struct, opts) do
changes = Map.take(struct, model.__schema__(:fields))
# If we have a primary key field, we should
# not include it in the list of changes.
if pk_field = model.__schema__(:primary_key) do
changes = Map.delete(changes, pk_field)
end
changeset = %Ecto.Changeset{model: struct, valid?: true, changes: changes}
update(repo, adapter, changeset, opts)
end
@doc """
Implementation for `Ecto.Repo.delete/2`.
"""
def delete(repo, adapter, %Ecto.Changeset{} = changeset, opts) when is_list(opts) do
struct = struct_from_changeset!(changeset)
model = struct.__struct__
source = model.__schema__(:source)
# There are no field changes on delete
changeset = %{changeset | repo: repo}
with_transactions_if_callbacks repo, adapter, model, opts,
~w(before_delete after_delete)a, fn ->
changeset = Callbacks.__apply__(model, :before_delete, changeset)
pk_filter = Planner.fields(:delete, model, pk_filter(model, struct))
{:ok, _} = adapter.delete(repo, source, pk_filter, opts)
Callbacks.__apply__(model, :after_delete, changeset).model
|> Map.put(:__state__, :deleted)
end
end
def delete(repo, adapter, %{__struct__: _} = struct, opts) do
delete(repo, adapter, %Ecto.Changeset{model: struct, valid?: true}, opts)
end
## Helpers
defp struct_from_changeset!(%{valid?: false}),
do: raise(ArgumentError, "cannot insert/update an invalid changeset")
defp struct_from_changeset!(%{model: nil}),
do: raise(ArgumentError, "cannot insert/update a changeset without a model")
defp struct_from_changeset!(%{model: struct}),
do: struct
defp load_into_changeset(%{changes: changes} = changeset, model, return, values) do
update_in changeset.model,
&model.__schema__(:load, struct(&1, changes), return, values)
end
defp merge_into_changeset(model, struct, fields, changeset) do
changes = Map.take(struct, fields)
pk_field = model.__schema__(:primary_key)
# If we have a primary key field but it is nil,
# we should not include it in the list of changes.
if pk_field && !Ecto.Model.primary_key(struct) do
changes = Map.delete(changes, pk_field)
end
update_in changeset.changes, &Map.merge(changes, &1)
end
defp validate_changes(kind, model, fields, changeset) do
Planner.fields(kind, model, Map.take(changeset.changes, fields))
end
defp pk_filter(model, struct) do
pk_field = model.__schema__(:primary_key)
pk_value = Ecto.Model.primary_key(struct) ||
raise Ecto.MissingPrimaryKeyError, struct: struct
[{pk_field, pk_value}]
end
defp with_transactions_if_callbacks(repo, adapter, model, opts, callbacks, fun) do
if Enum.any?(callbacks, &function_exported?(model, &1, 1)) do
{:ok, value} = adapter.transaction(repo, opts, fun)
value
else
fun.()
end
end
end