Nested cast_assoc calls not taking into account super parent id


I have a Receipt schema which has_many Items.
An Item also has_many :subitems which is the same Item schema.

The top level of items work as expected when using cast_assoc(:items) in the Receipt changeset as ecto is inserting the receipt_id into the correct field.
However, for sub-items, the receipt_id is empty since (I’m assuming) the sub-items are coming from the Item.changeset instead of the Receipt.changeset.

The input is like this:

  "receipt": {
    "items": [
        "title": "Best Coffee",
        "subitems": [ {"title": "Hazelnut syrup"} ],

Receipt (related code)

def new_changeset(receipt, attrs) do
    |> cast(attrs, available_attributes())
    |> cast_assoc(:items, with: &tem.new_changeset/2)
    |> shared_validations()

Item (related code)

def new_changeset(item, attrs) do
    |> cast(attrs, available_attributes())
    |> shared_validations()
    |> cast_assoc(:subitems, with: &Item.new_changeset/2)

The parent item gets the receipt_id field set correctly, while the sub-item has null in the receipt_id.
Any guidance is highly appreciated.

Can this be solved with cast_assoc at all or should I use something different like Ecto.Multi?

I would personally use Ecto.Multi there, or even just normal non-transactional calls unless there is a change of an item being inserted failing and the receipt should go too if that’s the case.

1 Like