marcin

marcin

Conditionally associate in a simple changeset() function

Hi! :wave:

I’d like to ask how you implement a super common functionality, for which I do not see any easy way in Ecto.Changeset

Lets say I have a Todo which belongs to a TodoList.

I get my auto-generated changeset() using phx.gen.schema which looks roughly like this:

  def changeset(todo, attrs) do

    todo
    |> cast(attrs, [:title, :details, :done])
    |> validate_required([:title])
  end

Now I would like to also be able to pass the association into this function, so in my context API todos.ex I can do:

def move_todo_to_list(todo, todo_list) do
   update_todo(todo, %{todo_list: todo_list})
end

obviouly in changeset() I cannot cast() the todo_list.
I need to call either put_assoc(), or change() - however, the association is not always given, I can also just have this usage from controller

def create_todo(params) do
  %Todo{} 
  |> Todo.changeset(params)
  |> Repo.insert()
end

where params would contain todo_list_id

I do not want to do any conditional handling in my changeset, nor defp some multiclause helpers:

  def changeset(todo, attrs) do
    chset = todo
    |> cast(attrs, [:title, :details, :done])
    
    # OMG verbose!!!!
    chset = if attrs[:todo_list] do
      put_assoc(chset, attrs[:todo_list], todo_list)
    else
      chset
    end

    chset
    |> validate_required([:title])
  end

(note, some custom validations might use the association to figure out if an attribute is valid or not, so validate goes last).

I used to do kinda elegant:

  def changeset(todo, attrs) do
    assocs = Map.take(attrs, [:todo_list])

    todo
    |> cast(attrs, [:title, :details, :done])
    |> change(assocs)
    |> validate_required([:title])
  end

But this will not work if attrs are sometimes keyed by strings (data from user), and sometimes by atoms (programmer controlled params from some module), and then when I add :todo_list key, i might end up with “cannot mix strings and atom keys” error from cast().

I bet this has to be solved somehow elegantly, this is such a common use case – but cannot come up with anything based on Ecto standard functions other then writing some custom helpers…

Most Liked

LostKobrakai

LostKobrakai

Tbh at a certain point it’s just not worth it trying to treat every possible scenario as “one and the same”. Ecto has useful APIs for all the usecases you mentioned. It’s however not going to be one API covering all of them at the same time.

Where Next?

Popular in Questions Top

vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
logicmason
Hi there, I'm working through my first release with elixir/phoenix. I've built a release with distillery and found that it crashes when I...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

Other popular topics Top

dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
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
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New

We're in Beta

About us Mission Statement