Kapeusz
Hello folks! I am trying to upload multiple files at once using Arc. I face such error: protocol Enumerable not implemented for %Plug.Upload when i try to create a product with attached file/s. Any idea what I am doing wrong here?
My schemas:
schema "products" do
field :colour, :string
field :description, :string
field :name, :string
field :price, :decimal
field :product_code, :string
field :release_year, :integer
belongs_to :category, Jordaniva.Categories.Category
belongs_to :subcategory, Jordaniva.Categories.Subcategory
belongs_to :type, Jordaniva.Inventory.Type
has_many :items, Jordaniva.Inventory.Item
has_many :photos, Jordaniva.Gallery.Photo
timestamps()
end
@doc false
def changeset(struct, attrs \\ %{}) do
struct
|> cast(attrs, [:name, :description, :price, :colour, :product_code, :release_year, :category_id, :subcategory_id, :type_id])
|> cast_assoc(:items)
|> cast_assoc(:photos)
|> validate_required([:name, :description, :price, :colour, :product_code, :release_year, :category_id, :subcategory_id, :type_id])
end
schema "photos" do
field :photo, Jordaniva.Photo.Type
field :uuid, :string
belongs_to :product, Jordaniva.Inventory.Product
timestamps()
end
@doc false
def changeset(photo, attrs) do
photo
|> Map.update(:uuid, Ecto.UUID.generate, fn val -> val || Ecto.UUID.generate end)
|> cast_attachments(attrs, [:photo])
|> validate_required([:photo])
end
Product controller (new/create):
def new(conn, _params) do
changeset = Product.changeset(%Product{items: [%Item{}, %Item{}]})
categories = Repo.all(Category) |> Enum.map(&{&1.name, &1.id})
subcategories = Repo.all(Subcategory) |> Enum.map(&{&1.name, &1.id})
render(conn, "new.html", changeset: changeset, categories: categories, subcategories: subcategories)
end
def create(conn, %{"photos" => product_params}) do
case Inventory.create_product(product_params) do
{:ok, product} ->
conn
|> put_flash(:info, "Product created successfully.")
|> redirect(to: Routes.product_path(conn, :show, product))
{:error, %Ecto.Changeset{} = changeset} ->
categories = Repo.all(Category) |> Enum.map(&{&1.name, &1.id})
subcategories = Repo.all(Subcategory) |> Enum.map(&{&1.name, &1.id})
render(conn, "new.html", changeset: changeset, categories: categories, subcategories: subcategories)
end
end
Photo controller:
def new(conn, _params) do
changeset = Gallery.change_photo(%Photo{})
render(conn, "new.html", changeset: changeset)
end
def create(conn, %{"product_id" => product_id, "photo" => photo_params}) do
product = Jordaniva.Inventory.get_product!(product_id)
case Gallery.create_photo(photo_params) do
{:ok, _photo} ->
conn
|> put_flash(:info, "Photo created successfully.")
|> redirect(to: Routes.photo_path(conn, :index))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "new.html", changeset: changeset)
end
end
Gallery.ex
def create_photo(%Product{} = product, attrs \\ %{}) do
%Photo{}
|> Photo.changeset(attrs)
|> Ecto.Changeset.put_assoc(:product, product)
|> Repo.insert()
end
def create_photos(%Product{} = product, attrs \\ %{}) do
Enum.each attrs["photos"], fn p ->
create_photo(product, %{"photo" => p})
end
end
end
Uploaders/photo.ex
defmodule Jordaniva.Photo do
use Arc.Definition
use Arc.Ecto.Definition
@extension_whitelist ~w(.jpg .jpeg .gif .png)
@versions [:primary]
@acl :public_read
def default_url(:primary, _movie) do
"http://placehold.it/350x200"
end
# To add a thumbnail version:
@versions [:original, :thumb]
# Whitelist file extensions:
def validate({file, _}) do
file_extension = file.file_name |> Path.extname() |> String.downcase()
Enum.member?(@extension_whitelist, file_extension)
end
# Define a thumbnail transformation:
def transform(:thumb, _) do
{:convert, "-strip -thumbnail 150x150^ -gravity center -extent 150x150"}
end
# Override the persisted filenames:
def filename(version, {file, scope}) do
"#{scope.uuid}_#{version}"
end
# Override the storage directory:
def storage_dir(version, {file, scope}) do
"uploads/photos/"
end
end
And full error:
[info] POST /products
[debug] Processing with JordanivaWeb.ProductController.create/2
Parameters: %{"_csrf_token" => "DQAuSi4VWwZsAgQxSltSVwYCMSkSTnR2_b_ggd30Us4dz-jesRTyb897", "photos" => %{"category_id" => "1", "colour" => "test66", "description" => "test66", "name" => "test66", "photos" => [%Plug.Upload{content_type: "image/png", filename: "Zrzut ekranu 2020-07-20 o 22.46.06.png", path: "/var/folders/1t/q27498654m376jz6fl02fqd80000gn/T//plug-1597/multipart-1597933231-847704270337599-3"}], "price" => "666", "product_code" => "test66", "release_year" => "2004", "subcategory_id" => "1", "type_id" => "1"}}
Pipelines: [:browser]
[debug] QUERY OK source="types" db=0.3ms idle=1764.5ms
SELECT t0."id", t0."name", t0."inserted_at", t0."updated_at" FROM "types" AS t0 ORDER BY t0."name" []
[info] Sent 500 in 32ms
[error] #PID<0.1214.0> running JordanivaWeb.Endpoint (connection #PID<0.1213.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: POST /products
** (exit) an exception was raised:
** (Protocol.UndefinedError) protocol Enumerable not implemented for %Plug.Upload{content_type: "image/png", filename: "Zrzut ekranu 2020-07-20 o 22.46.06.png", path: "/var/folders/1t/q27498654m376jz6fl02fqd80000gn/T//plug-1597/multipart-1597933231-847704270337599-3"} of type Plug.Upload (a struct). This protocol is implemented for the following type(s): Ecto.Adapters.SQL.Stream, Postgrex.Stream, DBConnection.Stream, DBConnection.PrepareStream, HashSet, Range, Map, Function, List, Stream, Date.Range, HashDict, GenEvent.Stream, MapSet, File.Stream, IO.Stream
(elixir 1.10.4) lib/enum.ex:1: Enumerable.impl_for!/1
(elixir 1.10.4) lib/enum.ex:141: Enumerable.reduce/3
(elixir 1.10.4) lib/enum.ex:3383: Enum.reduce/3
(arc_ecto 0.11.3) lib/arc_ecto/schema.ex:58: Arc.Ecto.Schema.convert_params_to_binary/1
(jordaniva 0.1.0) lib/jordaniva/gallery/photo.ex:19: Jordaniva.Gallery.Photo.changeset/2
(ecto 3.4.5) lib/ecto/changeset.ex:829: anonymous fn/4 in Ecto.Changeset.on_cast_default/2
(ecto 3.4.5) lib/ecto/changeset/relation.ex:128: Ecto.Changeset.Relation.do_cast/6
(ecto 3.4.5) lib/ecto/changeset/relation.ex:338: Ecto.Changeset.Relation.map_changes/9
(ecto 3.4.5) lib/ecto/changeset/relation.ex:112: Ecto.Changeset.Relation.cast/5
(ecto 3.4.5) lib/ecto/changeset.ex:800: Ecto.Changeset.cast_relation/4
(jordaniva 0.1.0) lib/jordaniva/inventory/product.ex:29: Jordaniva.Inventory.Product.changeset/2
(jordaniva 0.1.0) lib/jordaniva/inventory.ex:67: Jordaniva.Inventory.create_product/1
(jordaniva 0.1.0) lib/jordaniva_web/controllers/product_controller.ex:28: JordanivaWeb.ProductController.create/2
(jordaniva 0.1.0) lib/jordaniva_web/controllers/product_controller.ex:1: JordanivaWeb.ProductController.action/2
(jordaniva 0.1.0) lib/jordaniva_web/controllers/product_controller.ex:1: JordanivaWeb.ProductController.phoenix_controller_pipeline/2
(phoenix 1.5.4) lib/phoenix/router.ex:352: Phoenix.Router.__call__/2
(jordaniva 0.1.0) lib/jordaniva_web/endpoint.ex:1: JordanivaWeb.Endpoint.plug_builder_call/2
(jordaniva 0.1.0) lib/plug/debugger.ex:132: JordanivaWeb.Endpoint."call (overridable 3)"/2
(jordaniva 0.1.0) lib/jordaniva_web/endpoint.ex:1: JordanivaWeb.Endpoint.call/2
(phoenix 1.5.4) lib/phoenix/endpoint/cowboy2_handler.ex:65: Phoenix.Endpoint.Cowboy2Handler.init/4
Trending in Questions
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
Hello!
Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app.
I creat...
New
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
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
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
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted”
Version...
New
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
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
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
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
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #blog-post
- #ai
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming











Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
jeroenbourgois
Have you found something in the mean while? If not, I will try to dive in.
al2o3cr
The error you’re getting points to this code in
arc_ecto:https://github.com/stavro/arc_ecto/blob/1623e67076bec38efd40929fab3ac7c8f258b476/lib/arc_ecto/schema.ex#L57-L69
and suggests that it’s getting a
Plug.Uploadstruct instead of a map%{"photo": %Plug.Upload{}}Try
IO.inspecting things until you find where the parameter shape doesn’t match what you expect.Kapeusz
Sadly no, I have not found solution.
Kapeusz
Thanks for your reply, I will
IO.inspectmy code and try to find where the parameters don’t match.Kapeusz
I’ve made tiny changes to my code, and right now, I do not see any error message, but the problem is I cannot upload any file/s. When I add a new image and click save it shows “Oops, something went wrong! Please check the errors below.” but doesn’t display anything below. In the terminal, it looks like that:
IO.inspect in photo controller doesn’t show anything, only in the product’s controller:
al2o3cr
This indicates the request went down the
{:error, %Ecto.Changeset{} = changeset}path in the controller. What errors are in that changeset?al2o3cr
To clarify - in your photo controller’s create action:
Kapeusz
When I put this piece of code in my photo controller create action, there is no output like no parameters are passed. When I
IO.inspectproduct controller create action I got this output.I see the error that
errors: [photo: {"can't be blank"but I always try to upload some image.al2o3cr
What you’re uploading isn’t showing up where the changeset expects it, so it’s being ignored.
Your parameters are shaped like (from the logs):
The outer
"photos"wrapper is removed by the pattern-match inProductController.createThe next
"photos"wrapper is removed bycast_assoc(:photos), which iterates over each element and callsPhoto.changesetin this case, there is one element:
But
Photo.changesetis expecting a map like:Kapeusz
Ohh, I see. Could you tell me/give a hint where I should start with reshaping my code or how do I fix that?