Implementing a JSON field for an Ecto model

I’m faced with a problem of needing to store information that I can’t control the values of. I have a “Question” model and I need a field that essentially is a map that can store any value.

In Rails I would typically just make a JSON column and throw anything in that column that I want. This is the behavior I desire for this problem but I’m having trouble implementing it.

I’m using “embeds_one” on my “Question” model for the “information” field. I’ve never used embeds_one before so I may be approaching this completely wrong. Here is my current code:

QUESTION.ex:

 schema "questions" do
    field(:active, :boolean)
    field(:type, :string)
    field(:description, :string)
    embeds_one :information, MyApp.Information

    timestamps()
  end

  @required_fields ~w(active type description)a

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, @required_fields)
    |> cast_embed(:information)
    |> validate_required(@required_fields)
  end

INFORMATION.ex:

  embedded_schema do
    field :criteria
  end

  @required_fields ~w(criteria)a

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, @required_fields)
    |> validate_required(@required_fields)
  end

TEST:

valid_attrs %{
    active: true,
    description: "Player Points",
    type: "NBA",
    information: %{
      criteria: %{
        Player_name: "LeBron James"
      }
    }
  }

  test "changeset with valid attributes" do
    changeset = Question.changeset(%Question{}, @valid_attrs)
    assert changeset.valid?
  end

ERROR:

   information: #Ecto.Changeset<
      action: :insert,
      changes: %{},
      errors: [criteria: {"is invalid", [type: :string, validation: :cast]}],
      data: #MyApp.Information<>,
      valid?: false
    >

So, it seems that the that it is expecting a string not a map. But in this particular case I want criteria to essentially be able to hold anything I pass to it, much like a JSON field would behave.

MAIN QUESTION: How can I implement a JSON column that lets me put essentially anything in map for that attribute?

I think you’re just forgetting to declare the type of :map for your criteria field in the embedded schema

3 Likes