I am trying to show some related data in a form. I have a Students schema
schema "students" do
field :firstname, :string
field :lastname, :string
field :birthday, Ecto.Date
field :joined_on, Ecto.Date
field :gender, :string
timestamps()
end
Each student can have a mother and a father, these I have designated them as Parents and created a separate schema and separate table. Each parent record can be linked with max 1 student (odd, but works for my use case).
schema "parents" do
field :relationship, :string
field :fullname, :string
field :qualification, :string
belongs_to :student, Myapp.Student, foreign_key :student_id
timestamps()
end
I am trying to create a form where I can create new Students and Parents at the same time. So there will be fields for Student and fields for Mother and Father in the same form. I've tried multiple ways to get this Parents data in the eex template but I'm failing at it.
Here is what I'm using in the new
method of the Student
controller:
def new (conn, _params) do
changeset = Student.changeset(%Student{})
render(conn, "new.html", [changeset: changeset])
end
I think there's something wrong here because I'm not able to get the parents
fields in the eex form when I use the Html helpers. I want to be able to create new students, parents at the same time. Also it would be great if I can edit them using the same eex template. Thanks.
You need to use has_many
and cast_assoc
in Student
model:
defmodule Myapp.Student do
use Myapp.Web, :model
schema "students" do
field :firstname, :string
field :lastname, :string
field :birthday, Ecto.Date
field :joined_on, Ecto.Date
field :gender, :string
has_many :parents, Myapp.Parent
timestamps()
end
@doc """
Builds a changeset based on the `struct` and `params`.
"""
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:firstname, :lastname, :birthday, :joined_on, :gender])
|> cast_assoc(:parents)
|> validate_required([:firstname, :lastname, :birthday, :joined_on, :gender])
end
end
In web/templates/student/form.html.eex
use inputs_for
:
<%= inputs_for f, :parents, fn pf -> %>
<h3>Parent form</h3>
<div class="form-group">
<%= label pf, :relationship, class: "control-label" %>
<%= text_input pf, :relationship, class: "form-control" %>
<%= error_tag pf, :relationship %>
</div>
<div class="form-group">
<%= label pf, :fullname, class: "control-label" %>
<%= text_input pf, :fullname, class: "form-control" %>
<%= error_tag pf, :fullname %>
</div>
<div class="form-group">
<%= label pf, :qualification, class: "control-label" %>
<%= text_input pf, :qualification, class: "form-control" %>
<%= error_tag pf, :qualification %>
</div>
<% end %>
You can build two parents in Student controller action new
:
alias Myapp.Parent
def new(conn, _params) do
changeset = Student.changeset(%Student{parents: [%Parent{}, %Parent{}]})
render(conn, "new.html", changeset: changeset)
end
Also need to preload :parents
in StudentController
for edit
and update
actions:
student = Repo.get!(Student, id) |> Repo.preload(:parents)
The code - https://github.com/shhavel/stackoverflow-questions-39683691