Ecto Multi help needed

I have a page where admin creates a product along with adding multiple images and videos.
I am uploading multiple images and videos to s3 bucket. So i get 3 struct like below

Product Struct

%{name: "TV", description: "32 inch LED TV", admin_id: "some_binary_id"}



Product Image Struct

[
	%{image_name: "image_1.jpg"},
	%{image_name: "image_2.jpg"}
]

Product Video Struct

[
	%{video_name: "video_1.mp4"},
	%{video_name: "video_2.mp4"}
]

I have this 3 schema

Product Schema:

  schema "products" do
    field :description, :string
    field :name, :string
    field :admin_id, :binary_id

    timestamps()
  end

Product Image Schema:

  schema "product_images" do
    field :image_name, :string
    field :product_id, :id

    timestamps()
  end

Product Video Schema

  schema "product_videos" do
    field :video_name, :string
    field :product_id, :id

    timestamps()
  end

So first I need to insert product struct. Get the product id and insert in the map of the product image and product video struct and insert in DB

I can do this in 3 queries but I have been looking into Ecto multi, which seems interesting. Can anyone give me insight on how to achieve this in Ecto multi . Your help is greatly appreciated.

Can I do the image and video upload to s3 in the Ecto Multi itself and then insert things in DB?

I checked the Ecto Multi docs but somehow could not able to figure things out from it

I am trying like this but not getting output. Can anyone give me insight on this?

image_params = %{image_name: "test.jpg"}

multi_struct = Multi.new()
|> Multi.insert(:product, Product.changeset(%Product{}, %{name: "TV", description: "Just a test"}))
|> Multi.run(:image, fn %{product: product} ->
    Images.changeset(Images, image_params)
    |> Ecto.Changeset.put_assoc(product)
    |> Repo.insert 
  end)

What does it mean you’re not getting any output?

The generated Multi struct is inert, you need to pass it to Repo.transaction/1 for it to actually be executed.

image_params = %{image_name: "test.jpg"}

multi_struct = Multi.new()
|> Multi.insert(:product, Product.changeset(%Product{}, %{name: "TV", description: "Just a test"}))
|> Multi.run(:image, fn %{product: product} ->
    Images.changeset(Images, image_params)
    |> Ecto.Changeset.put_assoc(product)
    |> Repo.insert 
  end)
     |> Repo.transaction()

Sorry i mssed to paste it. But i am getting this error

no function clause matching in Ecto.Multi.run/3

The rest of the error and traceback would help :wink:

The second argument to Multi.run (the callback) receives two arguments, not one. The repo and the map of operations.

So it should read:

|> Multi.run(:image, fn _repo, %{product: product} ->
   ...
end)
2 Likes

This query is good

    image_params = %{image_name: "test.jpg"}
    
    multi_struct = Multi.new()
    |> Multi.insert(:product, Product.changeset(%Product{}, %{name: "TV", description: "Just a test", admin_id: "773283bd-ee99-48d8-bc47-f5b62321dd6d"}))
    |> Multi.run(:image, fn _repo, %{product: product} ->
        Images.changeset(%Images{}, image_params)
        |> Ecto.Changeset.put_assoc(product)
        |> Repo.insert 
      end)
    |> Repo.transaction

But i am getting " function Ecto.Changeset.put_assoc/2 is undefined or private" Any idea?

Fixed thanks

|> Ecto.Changeset.put_assoc(:products, product)

image_params = %{image_name: "test.jpg"}

multi_struct = Multi.new()
|> Multi.insert(:product, Product.changeset(%Product{}, %{name: "TV", description: "Just a test", admin_id: "773283bd-ee99-48d8-bc47-f5b62321dd6d"}))
|> Multi.run(:image, fn _repo, %{product: product} ->
    Images.changeset(%Images{}, image_params)
    |> Ecto.Changeset.put_assoc(:products, product)
    |> Repo.insert # returns a {:ok, user} or {:error, changeset}
  end)
|> Repo.transaction