I have a query in a Phoenix controller which gets a list of students, and for each row in the list does a join with the bloodgroup on its id and gets the bloodgroup name. I would like to display this in an eex template that I generated but am getting errors.
Here's the Student schema:
schema "students" do
field :firstname, :string
field :lastname, :string
field :birthday, Ecto.Date
field :joined_on, Ecto.Date
field :bloodgroup_id, :integer
timestamps()
end
Here's the BloodGroup model:
schema "bloodgroups" do
field :name, :string
timestamps()
end
I'm getting the data in the controller like so:
def query do
query = from s in Student,
join: b in BloodGroup, on: s.bloodgroup_id == b.id,
select: [s.firstname, s.lastname, s.birthday, s.joined_on, s.bloodgroup_id, b.name]
end
def index(conn, _params) do
students = Repo.all(query)
render(conn, "index.html", student_info: students)
end
And displaying in the template like so:
<%= for student <- @student_info do %>
<tr>
<td><%= Enum.fetch(student, 0) %></td>
<td><%= student[:lastname] %></td>
<td><%= student[:birthday] %></td>
<td><%= student[:joined_on] %></td>
<td><%= student[:name] %></td>
<td class="text-right">
<%= link "Show", to: student_path(@conn, :show, student), class: "btn btn-default btn-xs" %>
<%= link "Edit", to: student_path(@conn, :edit, student), class: "btn btn-default btn-xs" %>
<%= link "Delete", to: student_path(@conn, :delete, student), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-xs" %>
</td>
</tr>
<% end %>
But the using the form Enum.fetch
and student[:firstname]
don't work and throw either Argument error
or protocol Phoenix.HTML.Safe not implemented
I want to show what has been sent from the controller in the template but am hitting these errors. Is there a better way to do this? I'm new to Phoenix and Elixir.
Based on the query you've posted, you should be getting back a list of lists with Repo.all
. You can use pattern matching to extract the data, something like:
<%= for [firstname, lastname, birthday, joined_on, bloodgroup_id, name] <- @student_info do %>
BUT, that's not idiomatic. You really should be using Ecto's relationships here.
Add BloodGroup
as a belongs_to
relation to Student
:
schema "students" do
...
belongs_to :bloodgroup, BloodGroup, foreign_key: :bloodgroup_id
end
Then do the query like this:
students = Repo.all(Student) |> Repo.preload(:bloodgroup)
Then your template simply becomes:
<%= for student <- @student_info do %>
<tr>
<td><%= student.lastname %></td>
<td><%= student.birthday %></td>
<td><%= student.joined_on %></td>
<td><%= student.bloodgroup.name %></td>
<td class="text-right">
<%= link "Show", to: student_path(@conn, :show, student), class: "btn btn-default btn-xs" %>
<%= link "Edit", to: student_path(@conn, :edit, student), class: "btn btn-default btn-xs" %>
<%= link "Delete", to: student_path(@conn, :delete, student), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-xs" %>
</td>
</tr>
<% end %>
All code is untested. Please let me know if this doesn't work. It's probably a typo somewhere.