geonnave

geonnave

Hi, I am confused about different behavior in changesets when input data has atom vs string keys.

The Scenario

For example, let’s say I have a Company schema and changeset:

schema "company" do
  email: :string
  many_to_many: :categories, join_through: "companies_categories"
  has_many :addresses, Addresses.Address
end
def changeset(attrs) do
  %Company{}
  |> cast([:email])
  |> cast_assoc(:address)
end

I am omitting the definition of Address schema for the sake of brevity.

Then, considering I have the following data arriving at the controller:

%{
  "company" => %{
    "email" => "some@email.com",
    "categories_ids" => [1, 2],
    "address" => %{
      "address_line1" => "some address_line1"
      "city_id" => 2
    }
  }
}

The controller will receive it and call a function defined in my context module that will do the hard work:

# in CompanyController:
def create_company(conn, %{"company" => attrs}) do
  Accounts.create_company(attrs)
  # ...
end

# in Accounts module
def create_company(attrs) do
  company_changeset = Company.changeset(attrs)
  categories_ids_changeset = categories_ids_changeset(attrs)
  # ... create the company,
  #      obtain the categories_ids from its changeset,
  #        retrieve the categories from db using the ids,
  #        and put_assoc(:categories, company) 
end

def categories_ids_changesest(attrs) do
  types = %{categories_ids: {:array, :integer}}

  {attrs, types}
  |> cast(attrs, Map.keys(types))
end

In this case I am using a schemaless changeset to validate input data that is slightly different from data in my schema.

The struggle:

I can not find a standard way to retrieve the changes from categories_ids_changeset.

So, ok, I can do categories_ids_changeset.changes. That will work when the input data has string keys. But it will not if it has atom keys. See:

iex(80)> attrs = %{"categories_ids" => [1, 2]}
iex(81)> types = %{categories_ids: {:array, :integer}}
iex(82)> changeset = {attrs, types} |> Changeset.cast(attrs, Map.keys(types))
#Ecto.Changeset<action: nil, changes: %{categories_ids: [1, 2]}, errors: [],
 data: %{"categories_ids" => [1, 2]}, valid?: true>
iex(84)> attrs = %{categories_ids: [1, 2]}
iex(85)> types = %{categories_ids: {:array, :integer}}
iex(86)> changeset = {attrs, types} |> Changeset.cast(attrs, Map.keys(types))
#Ecto.Changeset<action: nil, changes: %{}, errors: [],
 data: %{categories_ids: [1, 2]}, valid?: true>

Note that in the second changeset, whose initial attrs had atom keys, changes is empty!

I tried to appy_changes/1 in the changeset, but when there are string keys, it will generate a map with mixed keys…

%{:categories_ids => [1, 2], "categories_ids" => [1, 2]}

This behavior is messing up with my tests, as for testing CompanyController.create_company/2, I pass data with string keys, but when testing my context modules, i.e. Accounts.create_company, I tend to use atom keys.

I thought about using apply_changes/1 and they filter out string keys… but I don’t know, it seems hacky, so maybe I am missing something simpler that can solve it in a better way.

Thanks in advance.

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews