Henricus Louwhoff

Ecto Changeset in Contexts

A while back I read on the elixir forum that Saša Jurić prefers to have the Ecto changeset functions, as private functions, in the contexts rather than the schemas themselves.

After experimenting with it in a personal project, it started to make sense. While the documentation of both Ecto and Phoenix suggests to place the changeset functions in the schemas, it actually makes more sense to me to place them in the contexts. Contrary to Saša suggestion, I like to make the changeset functions not private and make the @doc attribute false.

In another blog post Tracy show how using multiple changesets work better in Elixir (still in the schema though).

Both those posts made me want to try this out and I'm pleased with the end result:

defmodule MyApp.Orders do
@moduledoc "Orders context"

import Ecto.Changeset

@spec create_order(map, Shop.t()) :: {:ok, Order.t()} | {:error, Ecto.Changeset.t(Order.t())}
def create_order(params, shop) do
shop
|> Ecto.build_assoc(:orders)
|> order_changeset(params)
|> put_order_number_change() # only needed when creating an order
|> (...) # more create related changeset operations
|> Repo.insert()
end

# General changeset that can be used on create and update
@doc false
@spec order_changeset(Order.t(), map) :: Ecto.Changeset.t(Order.t())
def order_changeset(order, params) do
order
|> cast(params, [:email, ...])
|> validate_required([:email, ...])
end
end

Tags: