Ebtoulson
JSON validation with defaults
I’ve seen a few examples on using Ecto.Schema for validations but I can’t seem to find a way to make defaults play nicely. For some background, I’m building a API that aggregate and cleans data before POSTing it to an external HTTP service. Since the external service has tons of optional params and others that I can provide defaults to I wanted to use a_emphasized text_ schema.
defmodule Lead do
use Ecto.Schema
import Ecto.Changeset
@required_fields ~w(device type_id website)a
@primary_key false
embedded_schema do
field :device, :string
field :type_id, :integer, default: 0
field :session_id, :string
field :website, :string, default: "facebook"
field :message, :string
end
def changeset(%__MODULE__{} = lead, params \\ %{}) do
lead
|> cast(params, __schema__(:fields))
|> cast_session_id
|> validate_required(@required_fields)
|> validate_inclusion(:device, ["web", "mobile"])
|> validate_inclusion(:type_id, 0..6)
end
defp cast_session_id(changeset) do
case get_field(changeset, :session_id) do
nil ->
unix_time =
DateTime.utc_now
|> DateTime.to_unix(:milliseconds)
put_change(changeset, :session_id, "no_session_id_#{unix_time}")
_ -> changeset
end
end
end
This works fine for the most part but when trying to get the final map I run into some weirdness.
iex(1)> cs = %Lead{} |> Lead.changeset(%{device: "web"})
#Ecto.Changeset<action: nil,
changes: %{device: "web", session_id: "no_session_id_1503097557106"},
errors: [], data: #Lead<>, valid?: true>
iex(2)> cs.changes
%{device: "web", session_id: "no_session_id_1503097557106"}
iex(3)> Ecto.Changeset.apply_changes(cs)
%Lead{device: "web", message: nil, session_id: "no_session_id_1503097557106",
type_id: 0, website: "facebook"}
If I try and use the changes, then field defaults don’t work.
If I try to apply_changes I get fields with nil.
I see two options, I could either move the default into a cast like cast_session_id or I could just filter out keys with nil values. The real schema has 15 optional fields with 5 or so fields that I could provide defaults.
Thoughs / Suggestions?
Marked As Solved
OvermindDL1
This is what you should do, and message is nil because it has no default value and it was not set. If it should be set by the user then you need to add it to the validate_requited call. ![]()
Also Liked
Ebtoulson
Thanks for the reply.
I thought about this a little more over the weekend and it does make sense for apply_changes to return fields with nil, in the context of struct access. I guess I was stuck thinking about it as post params, where you wouldn’t necessarily want to pass in nil for optional fields.
For this request, nil can only mean missing or unset so I should be good if I filter out nil.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex









