I have been trying to figure this out for a few hours now.
The problem
When i use a decimal field in a phoenix for, i see this message
“lists in Phoenix.HTML and templates may only contain integers representing bytes, binaries or other lists, got invalid entry: #Decimal<12>”
I have a an ecto query that returns a decimal field. The query looks like this
query = from o in OrderSupplies,
join: s in Supply, where: s.id == o.supply_id,
select: [ o.id, s.product_name, o.supply_id, o.qty_ordered, o.price],
where: o.order_id == ^id,
order_by: [desc: o.inserted_at]
o.price is the decimal field. The data base is mysql.
I am trying to display the price field in an index page in this way
<%= supply.price)) %>
I also tried these two variations. It didn’t work
<%
The to string should work but why do you have two pairs of <% around it? A single one with the equal sign should be sufficient…
If removing the outer ones does not help, please give us a bit more context, your error message tells something about “for” which I do not see in your snippets.
It looks like you are trying to interpolate a Decimal struct from the Decimal module, but phoenix is saying that you can only interpolate “bytes, binaries, or other lists”.
EDIT: I just realized that your Decimal.to_string(Decimal.new(supply.price)) should effectively be the same thing, so maybe that isn’t it.
EDIT2: The error specifically says “lists in Phoenix.HTML and templates”, so maybe your query returned a list with a single item. You might try <%= supply.price |> Lists.first |> to_string %> and see if that works. If that does then you probably want to extract the item from the lists before passing it to the template, just to keep the template concerned with only printing values.
@NobbZ: The extra <% was a typo, i missed removing it when i created the post. Same with this line just below it… “<% <%= Float.to_string(supply.price) %> %>” i forgot to remove it as well. Sorry for the confusion.
@kylethebaker: You are correct, the column value returned from the ecto query seems to be a decimal structure. And no, this did not work either ‘<%= supply.price |> Lists.first |> to_string %>’
Here is some more context: I am trying to display a money value in a phoenix form with a value from the database which happens to be a decimal. So it appears Ecto is doing what it is supposed to do.
I googled again after i posted this question and see that the general wisdom is to use a bigint instead of decimal and store currency as cents. So perhaps i should switch to using bigint? Your thoughts?
I had a typo in my reply, it should be List.first instead of Lists.first. Ecto.all()returns a list, and the error message indicates that it is trying to interpolate of a list of Decimal schemas. Give this another shot now that it’s corrected:
Okay, your problem is not the stringification of the Decimal in the table. Its your single line of <%= @orders_supplies %>, removing it or properly converting it into a string (inspect as a start?) should be sufficient.
Explanation:
You gave it a list, simplified: [[64, "String", #Decimal<12>]], which is recursively tried to get interpreted as io_data. The number 64 is interpreted as Unicode (which represents the @), the string is interpreted directly, and Decimal<12> fails.
PS: Also instead of pasting screenshots, it would be nice if you could just copy and paste the output. Its much nicer to read, as it adheres to the colorschema of the board and my user-CSS, I had a hard time to actually read the screenshot as the colors are not what I consider an optimum.
I think @NobbZ is right, but I think I’m seeing another error here. In the ecto query you are selecting into a list, but what you really want is an OrderSupply struct I think. You can see both in the code and the screenshot that it’s returning a nested list:
Then you pass your query to Repo.all() which gives you a list of maps. Then you need to convert each one of those maps to your OrderSupply struct. Enum.map is one way to do that (calling struct(module, map) will create a struct from a map for that module).
@kylethebaker i tried this by changing the ecto select clause to a map.
select: %{ o.id, s.product_name, o.supply_id, o.qty_ordered, o.price},
When i started mix i see a compilation error with this message
** (FunctionClauseError) no function clause matching in anonymous fn/2 in Ecto.Query.Builder.Select.escape_pairs/4
The following arguments were given to anonymous fn/2 in Ecto.Query.Builder.Select.escape_pairs/4:
# 1
{{:., [line: 335], [{:o, [line: 335], nil}, :id]}, [line: 335], []}
# 2
{%{}, %{}}
lib/ecto/query/builder/select.ex:86: anonymous fn/2 in Ecto.Query.Builder.Select.escape_pairs/4
so i guess i need to learn more about ecto queries and maps to make your example work for me. I decided to use this as a learning exercise for the weekend and for now switch from using decimal to bigint.
Many thanks to you and @nobbz for being patient and super helpful. I learned something new today about ecto and elixir maps.