1

I have this model with a has_many directive. I'm trying to get all the values out of it and display them in a json response:

defmodule Heroico.Datacenter do
    use Heroico.Web, :model

    @derive {Poison.Encoder, only: [:id, :identifier, :city, :state, :country]}
    schema "datacenters" do
        field :identifier, :string
        field :city, :string
        field :state, :string
        field :country, :string

        belongs_to :provider, Heroico.Provider
        has_many :plans, Heroico.Plan

        timestamps()
    end

    ...
end

Controller:

def index(conn, _params, _current_user, _claims) do
    render conn, "index.json", datacenters: Repo.all(Datacenter)
end

View:

defmodule Heroico.DatacenterView do
    use Heroico.Web, :view

    def render("index.json", %{datacenters: datacenters}) do
        %{datacenters: datacenters}
    end
end

lib/poison_encoder.ex:

defimpl Poison.Encoder, for: Any do
    def encode(%{__struct__: _} = struct, options) do
        map = struct
            |> Map.from_struct
            |> sanitize_map
        Poison.Encoder.Map.encode(map, options)
    end

    defp sanitize_map(map) do
        Map.drop(map, [:__meta__, :__struct__])
    end
end

As you can see, the @derive clearly states that only [:id, :identifier, :city, :state, :country] should be encoded yet I can't seem to get rid of this error:

cannot encode association :plans from Heroico.Datacenter to JSON because the association was not loaded. Please make sure you have preloaded the association or remove it from the data to be encoded

Romeo Mihalcea
  • 9,714
  • 12
  • 50
  • 102
  • I cannot reproduce this locally. Could you try running `mix clean` once and restart the Elixir process and see if this still happens? – Dogbert Mar 05 '17 at 19:41
  • Same result. I tried switch from `only` to `except` for the `@derive` but nothing changed. It's like the @derive is totally ignored. – Romeo Mihalcea Mar 05 '17 at 20:02
  • What does this return: `Poison.Encoder.impl_for(%Heroico.Datacenter{})`? – Dogbert Mar 05 '17 at 20:31
  • That returns `Poison.Encoder.Any` – Romeo Mihalcea Mar 05 '17 at 21:18
  • That confirms that `Poison.Decoder` is not being implemented for `Datacenter`. – Dogbert Mar 06 '17 at 02:18
  • Thanks for all the help so far. Any idea what may be causing this? – Romeo Mihalcea Mar 06 '17 at 09:43
  • Can you try reproducing this in a brand new Phoenix app? I copied the `@derive` line into one of my app and `impl_for` returns the correct module for my model. Also, if you implement `Poison.Encoder` for that model manually (like you did for `Any`: `defimpl Poison.Encoder, for: Heroico.Datacenter do`), does that work? – Dogbert Mar 06 '17 at 09:48
  • That prints out `Poison.Encoder.Heroico.Datacenter` but the error is still there `cannot encode association :plans`. I'll try with a new project and see if the error is still there. – Romeo Mihalcea Mar 06 '17 at 10:15
  • You'll have to `Map.drop` `:plans` (and `:provider`, etc) in the manual implementation. That should work. – Dogbert Mar 06 '17 at 10:20
  • Feels like a hack (since `@derive` is clearly not instructed to pull out `:plans`) but works. Can you make this an answer so I can accept it? – Romeo Mihalcea Mar 06 '17 at 10:38
  • Well, `Map.drop` is not a hack; that's how you'd implement `Poison.Encoder` manually (if that's what you meant by `feels like a hack`). For whatever reason, `@derive` is not working at all. Were you able to reproduce this `@derive` problem in a brand new project? – Dogbert Mar 06 '17 at 11:23

0 Answers0