Create Foreign Key Relationship Between Ecto Tables
This page is in progress
My goal is to create two html generated tables in Phoenix.
- Testbed(s)
- Group(s)
I want to create a relationship between these two tables so that:
- A Group can have many Testbeds
- Each Testbed can belong to 1 Group
- I intend to write front end code that presents this relationship to the user.
Steps
In Phoenix I generated the two tables and related UI code using these commands:
mix phx.gen.html Testbeds Testbed testbeds name:string mix phx.gen.html Groups Group groups name:string
The command line prompted me to add the resources / route for both of them. I did what it said.
I ran:
mix ecto.migrate
Testbed Migration For testbeds, I create a migration file and add this field: group_id:references:groups.
def change do alter table(:testbeds) do # customize to your code add :group_id, references(:groups) end end
Change Group Schema I update Group schema. Changes in Comments
defmodule App.Groups.Group do use Ecto.Schema import Ecto.Changeset alias App.Testbeds.Testbed # Alias! schema "posts" do field :body, :string field :title, :string has_many :testbeds, Testbed # Has many timestamps() end @doc false def changeset(post, attrs) do post |> cast(attrs, [:title, :body]) |> validate_required([:title, :body]) end end
Change Testbed Schema
defmodule App.Testbeds.Testbed do use Ecto.Schema import Ecto.Changeset alias App.Groups.Group # Alias schema "testbeds" do field :name, :string belongs_to :group, Group # Belongs to timestamps() end @doc false def changeset(testbed, attrs) do testbed |> cast(attrs, [:name]) |> validate_required([:name]) end end
I read that I needed to incorporate “preload” so that I can submit data. So I did this:
def list_testbeds do Repo.all(Testbed) |> Repo.preload([:group]) end
and this
def list_groups do Repo.all(Group) |> Repo.preload([:testbeds]) end
When I query Groups they now look like this:
%App.Groups.Group{ __meta__: #Ecto.Schema.Metadata<:loaded, "groups">, id: 3, name: "FUNK", testbeds: [], inserted_at: ~N[2023-09-27 18:13:15], updated_at: ~N[2023-09-27 18:13:15] }
When I query Testbeds they now look like this:
%App.Testbeds.Testbed{ __meta__: #Ecto.Schema.Metadata<:loaded, "testbeds">, id: 1, name: "Bloop", group_id: nil, group: nil, inserted_at: ~N[2023-09-27 14:04:15], updated_at: ~N[2023-09-27 18:22:23] }
I now need to know how to write code that assigns a Testbed to a Group.
Submit Testbed with Group Association
group = App.Groups.get_group!(1) thing = Ecto.build_assoc(group, :testbeds, name: "DUMB") alias App.{Repo} Repo.insert(thing)