Why my preload associations is not working in Elixir Phoenix application

New bee to Elixir Phoenix, coming from Java background, this framework amazed me till now

Am creating a Parent Child table and trying to Preload Child data along with parent following the instructions provided, but somehow in the API End point response I only see the parent object values

Parent Table Schema

@primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id
  schema "parent" do
    field :name, :string
    has_many :child, Child
    timestamps(type: :utc_datetime)
  end

Child

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id
  schema "child" do
    field name, :string
    belongs_to :parent, Parent
    timestamps(type: :utc_datetime)
  end

Migration Parent Table

def change do
    create table(:parent, primary_key: false) do
      add :id, :binary_id, primary_key: true
      add :name, :string
      timestamps(type: :utc_datetime)
    end
  end

Migration Child Table

def change do
    create table(:child, primary_key: false) do
      add :id, :binary_id, primary_key: true
      add :name, :string
      add :parent_id, references(:parent, on_delete: :nothing, type: :binary_id)

      timestamps(type: :utc_datetime)
    end
  end

In the Repo file, here I was expecting child table data to be present along with Parent, but I dont see it

 def list_parent do
    Repo.all(Parent) |> Repo.preload(:child)
  end

When I run the Query Interactively iex -S mix, I can see the Parent and Child records populating, I tried all the options from google search, but still cant able to figure it out

Thanks Everyone

That should definitely work… the only thing different from what I would do is that the @foreign_key_type should not be necessary. I can’t see that it would do any harm but I’ve never used it myself when using :binary_id everywhere.

It’s possible there is something missing from code you aren’t sharing. For example, are Parent and Child the full names of the modules? If not, are they properly aliased in the schema?

I can’t really think of anything else atm.

Hi, thanks for the response. I have the right alias, I had changed the real table names into parent and child for better understanding, The same Repository queries works fine in iex -S mix terminal window, Is there anything in confix.exs must be made for preloading?

Can you show the code of your API endpoint that constructs the response?

Hi,

My router entry

resources "/parent", ParentController, except: [:new, :edit]

parent_controller.ex

defmodule MyAppWeb.ParentController do
  use MyAppWeb, :controller
def index(conn, _params) do
    parent = Parent.list_parent()
    render(conn, :index, parent : parent )
  end

Parent File
parent.ex

defmodule MyApp.Parent do
  @moduledoc """
  The Parents context.
  """
  import Ecto.Query, warn: false
  alias MyApp.Repo

  alias MyApp.Parent
.
.

  def list_parent do
    Repo.all(Parent) |> Repo.preload(:child)
  end

You don’t preload in the api

UPDATE: Yes You do… sorry
Please show the file that renders JSON :slight_smile:

Hi, these are the files I got

router → controller → parent obj(where I run Repo statements)

Depending of the Phoenix version, You should have a parent_json_ex in the controllers folder. Or a view if You use an older version

It could be named parent_html.ex if You used the wrong generator…

How did You create the controller and the resource? is it something like…

mix phx.gen.json ...
1 Like

Hi, Am sorry I find the Json file you mentioned, I guess here is the problem :slight_smile: i dont see reference to the child records

 defp data(%Parent{} = parent) do
    %{
      id: parent.id,
      name: parent.name
    }
  end
1 Like

yes, it’s probably this :slight_smile:

BTW there is also this
@timestamps_opts type: :utc_datetime

You could define your schema like this…

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id
  @timestamps_opts type: :utc_datetime
  schema "child" do
    field name, :string
    belongs_to :parent, Parent
    timestamps()
  end

and this

… and here I would probably choose children as the name of the association, there are many

2 Likes

I was able to fix it by referencing child records in parent and using Json.Encoder

@derive {Jason.Encoder, only: [:field]} and refer the child object in parent in _json file under we

My general advice would be to follow the pattern provided by phoenix with explicit build of the JSON, you can easily do that by creating another view, instead of defining the decoder in schema. The reason behind this is that there are lots of cases where your API responses are not 1:1 with your schemas.

4 Likes