0

I am a newbie to elixir. I have a Ecto Schema

  defmodule MyScoreSchema do
  use Ecto.Schema
  import Ecto.Changeset

  schema "historical_extra_fuels" do
    field :average, :float
    field :count, :float
    field :percent, :float
    field :name, :string
  end


  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:average, :count, :percent])
    |> validate_required([])
  end
end

and parent schema

defmodule OverallScore do
  use Ecto.Schema
  import Ecto.Changeset

  schema "OverallScore" do
    field :avg_pass, :float
    field :avg_fail, :float
    field :total_students, :float
    embeds_many :my_score_schema, MyScoreSchema
  end
  @required_fields ~w[]a
  @optional_fields ~w[avg_pass, avg_fail, total_students ]a


  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, @optional_fields, required: false )
    |> cast_embed(:my_score_schema, required: false)
  end
end

And have a HTTP REST API http://localhost:8080/getScoreData which gives data

{
      "avgPass": 85.55,
      "avgFail": 14.45,
      "totalStudents": 80.0,
      "myScoreSchema": [
        {
          "average": 80.0,
          "count": 8.0,
          "percent": 80.0,
          "name": "John"
        },
        {
          "average": 90.0,
          "count": 8.0,
          "percent": 90.0,
          "name": "Cena"
        },
        {
          "average": 80.0,
          "count": 8.0,
          "percent": 80.0,
          "name": "Sunny"
        },
        {
          "average": 70.0,
          "count": 8.0,
          "percent": 70.0,
          "name": "Michael"
        }
      ]
    }

and the code

  url = "http://localhost:8080/getScoreData"
   Logger.info("the url is #{url}")
   case HTTPoison.get(url) do
     {:ok, %{status_code: 200, body: body}} ->
       overall_score = Jason.decode!(body, as: [%OverallScore{}])
       {:ok, overall_score}
   end

This somehow works and don't give error but the result is some struct and not really OverallScore ecto schema object

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
veer7
  • 20,074
  • 9
  • 46
  • 74

1 Answers1

2

I would suggest instead of using that "as: [%OverallScore{}]" syntax, you use the changeset in your model that you already have. This would look something like this:

  url = "http://localhost:8080/getScoreData"
   Logger.info("the url is #{url}")
   case HTTPoison.get(url) do
     {:ok, %{status_code: 200, body: body}} ->
       response = Jason.decode!(body)
       overall_score = OverallScore.changeset(%OverallScore{}, response)
       {:ok, overall_score}
   end

Changesets are generally the best way to get objects into ecto structs as they will run through your changeset validation properly. This has the added benefit of it dropping anything in the "response" field that it isn't in the changeset's cast call without any errors. You'll also be able to quickly check if it's valid, and if so you can insert it into the database with your ecto repo.

Elliot Blackburn
  • 3,759
  • 1
  • 23
  • 42
  • The `overall_score` here is coming as `#Ecto.Changeset – veer7 Apr 17 '20 at 19:27
  • @veer7 if you just want to build the struct rather than acting with a changeset then you will want to use the "Kernel.struct/2" function as shown here - https://stackoverflow.com/a/41980865/2265722. As outlined in the answer linked you'll want to convert the map from string keys to atom keys first. – Elliot Blackburn Apr 18 '20 at 11:32