Ebtoulson

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

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. :slight_smile:

Also Liked

Ebtoulson

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.

Where Next?

Popular in Questions Top

Kurisu
For example for a current url like http://localhost:4000/cosmetic/products?_utf8=✓&amp;query=perfume&amp;page=2, I would like to get: ...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; somethi...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
Lily
In templates/appointment/index.html.eex: &lt;%= for appointment &lt;- @appointments do %&gt; &lt;tr&gt; &lt;td&gt;&lt;%= appoi...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New

Other popular topics Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

We're in Beta

About us Mission Statement