Error in ecto schema in virtual field referencing array of another schema

I have a schema member_prices and I would like to define a virtual field referencing to a Discount schema which I wanted to be an array. However this doesn’t work for me.

Schema:

schema "member_prices" do
    field(:name, :string)
    field(:duration, :integer)
    field(:service_fee, :decimal)
    field(:discounts, {:array, Discount}, virtual: true)
    timestamps(usec: false)
end

Error:

== Compilation error in file lib/../member_price.ex ==
** (ArgumentError) schema Rax.Discount is not a valid type for field :discounts. Did you mean to use belongs_to, has_one, has_many, embeds_one, or embeds_many instead?
    (ecto 3.9.5) lib/ecto/schema.ex:2271: Ecto.Schema.check_field_type!/4
    (ecto 3.9.5) lib/ecto/schema.ex:2254: Ecto.Schema.check_field_type!/4
    (ecto 3.9.5) lib/ecto/schema.ex:1919: Ecto.Schema.__field__/4
    lib/.../member_price.ex:10: (module)
    (elixir 1.14.2) lib/kernel/parallel_compiler.ex:346: anonymous fn/5 in Kernel.ParallelCompiler.spawn_workers/7

Am I missing something?

Rax.Discount would need to implement Ecto.Type, but given the error doesn‘t seem to do that.

1 Like

Do I have to implement Ecto.Type inside the Discount schema?

Discount Schema

defmodule Rax.Discount do
  use Ecto.Schema
  import Ecto.Changeset

  schema "discounts" do
    field(:fixed_amount, :decimal)
    field(:name, :string)
    field(:rate_amount, :decimal)
    field(:duration, :integer)

    timestamps()
  end

  @required_fields [:name, :duration]
  @optional_fields [:fixed_amount, :rate_amount]

  def changeset(discount, attrs) do
    discount
    |> cast(attrs, @required_fields ++ @optional_fields)
    |> validate_required(@required_fields)
  end
end

{:array, …} and {:map, …} composes with other ecto types, not schemas. You might be looking for has_many or many_to_many for relating rows of schemas to each other.

But this is just a virtual field. Do I need has_many relation for that as well?

Ah, there’s no virtual has_many or many_to_many. If you’re assigning those values at runtime (no casting involved) I’d just go with {:array, :any}. Otherwise I’d take a step back and ask what usecase you’re trying to cover with that field.

The above schema worked before when it was running in Phoenix 1.3 after upgrading to 1.6.14 I get the compilation error.

Not sure is there is some changes to Ecto altogether.

I can use {:array, :any} however it may need a major changes in the code in rendering the views I suppose.

I fixed the problem with the following

field :discounts, {:array, :map}, virtual: true

Used :map instead of :any.