Running associated changesets in put_assoc

I would like to insert Chatroom, ChatroomMembers and Message in one go.

I have the following changeset

  def create_changeset(attrs) do
    |> cast(attrs, [:name, :room_type])
    |> validate_required(@required_fields)
    |> put_assoc(:chatroom_members, attrs.chatroom_members)
    |> put_assoc(:messages, attrs.messages)

that I call as follows:

  room_type: :private,
  chatroom_members: members,
  messages: message
|> Chatroom.create_changeset()
|> Repo.insert()

Can we run the changesets for the associated records in put_assoc? (I am aware this is feasible with cast_assoc but it seems cast_assoc requires the parent to exist ahead of time)

Have you tried it? I’d say it should work :thinking:


As mentionned by @stefanchrobot, it should work.

You could also use Ecto.Multi and Repo.transaction.

1 Like
defmodule Chata.Chatroom do
  def changeset(attrs) do
    |> cast(attrs, [:name, :room_type])
    |> validate_required(@required_fields)
    |> cast_assoc(:chatroom_members)
    |> cast_assoc(:messages, with: &Message.create_changeset/2)

And then using it

iex(14)> members = [%{user_id:}, %{user_id:}]           
[%{user_id: 2034370041046106112}, %{user_id: 2034370071924572160}]
iex(15)> message = [%{user_id:, body: "hi"}]                      
[%{body: "hi", user_id: 2034370041046106112}]
iex(16)> Chatroom.changeset attrs                                
  action: nil,
  changes: %{
    messages: [
        action: :insert,
        changes: %{body: "hi", user_id: 2034370041046106112}, 
        errors: [chatroom_id: {"can't be blank", [validation: :required]}],
        data: #Chata.Message<>,
        valid?: false
    room_type: :private
  errors: [], 
  data: #Chata.Chatroom<>,
  valid?: false

Doesn’t the error chatroom_id can't be blank, mean a Chatroom record (the parent) needs to exist before being able to run cast_assoc ?

That error is added by validate_required because the attribute @required_fields contains chatroom_id


That was it, thanks. As I replied, I realized.

1 Like