Expected params to be a :map, got: `%Challenge.Comments.Comment{...}

Here are my schemas. A game can have multiples comments

defmodule Challenge.Games.Game do
  use Ecto.Schema
  import Ecto.Changeset
  alias Challenge.{Ratings, Comments}

  schema "games" do
    field :description, :string
    field :image, :string, default: ""
    field :name, :string
    field :promoted, :boolean, default: false
    field :tags, {:array, :string}, default: []
    has_many(:comments, Comments.Comment)
    has_many(:ratings, Ratings.Rating)

    timestamps()
  end

  @doc false
  def changeset(game, attrs) do
    game
    |> cast(attrs, [:name, :description, :image, :tags, :promoted])
    |> validate_required([:name, :description, :image])
  end
end
defmodule Challenge.Comments.Comment do
  use Ecto.Schema
  import Ecto.Changeset
  alias Challenge.Games.Game

  schema "comments" do
    field(:body, :string)
    belongs_to(:game, Game, foreign_key: :game_id)

    timestamps()
  end

  @doc false
  def changeset(comment, attrs) do
    comment
    |> cast(attrs, [:body])
    |> validate_required([:body])
  end
end

And here thre create method of my controller

  def create(conn, %{"comment" => comment_params}) do
    game = Games.get_game!(comment_params["game_id"])

    comment_changeset = Ecto.build_assoc(game, :comments, %{body: comment_params["body"]})
    IO.inspect(comment_changeset)

    case Comments.create_comment(comment_changeset) do
      {:ok, comment} ->
        conn
        |> put_flash(:info, "Comment created successfully.")
        |> redirect(to: Routes.game_path(conn, :show, comment_params["game_id"]))

      {:error, %Ecto.Changeset{} = changeset} ->
        redirect(conn, to: Routes.game_path(conn, :show, comment_params["game_id"]))
    end
  end

When I test the data type of the comment_changeset it’s a map so I don’t understand why I’ve got this error message :

expected params to be a :map, got: `%Challenge.Comments.Comment{__meta__: #Ecto.Schema.Metadata<:built, "comments">, body: "salut\r\n", game: #Ecto.Association.NotLoaded<association :game is not loaded>, game_id: 1, id: nil, inserted_at: nil, updated_at: nil}`

I tried inserting the changeset directly using Repo.insert() and it works but I can’t understand what is the problem with my schemas.

I believe that the argument to your Comment.create_comment function should be your comment_params.

I would take a look at the definition of your Comment.create_comment function to be sure, if it was generated by the phoenix generators, it definitely wants a map of params.

Please provide the full stack trace.

If I do this, my comment will not have the foreign key references the game :confused:
It was the default behaviour.

And when I test the data type of my comment_changeset it’s a map.

Of course :slight_smile:

[debug] ** (Ecto.CastError) expected params to be a :map, got: `%Challenge.Comments.Comment{__meta__: #Ecto.Schema.Metadata<:built, "comments">, body: "zafaz", game: #Ecto.Association.NotLoaded<association :game is not loaded>, game_id: 1, id: nil, inserted_at: nil, updated_at: nil}`
    (ecto) lib/ecto/changeset.ex:463: Ecto.Changeset.cast/4
    (challenge) lib/challenge/comments/comment.ex:16: Challenge.Comments.Comment.changeset/2
    (challenge) lib/challenge/comments.ex:54: Challenge.Comments.create_comment/1
    (challenge) lib/challenge_web/controllers/comment_controller.ex:33: ChallengeWeb.CommentController.create/2
    (challenge) lib/challenge_web/controllers/comment_controller.ex:1: ChallengeWeb.CommentController.action/2
    (challenge) lib/challenge_web/controllers/comment_controller.ex:1: ChallengeWeb.CommentController.phoenix_controller_pipeline/2
    (phoenix) lib/phoenix/router.ex:280: Phoenix.Router.__call__/2
    (challenge) lib/challenge_web/endpoint.ex:1: ChallengeWeb.Endpoint.plug_builder_call/2
    (challenge) lib/plug/debugger.ex:122: ChallengeWeb.Endpoint."call (overridable 3)"/2
    (challenge) lib/challenge_web/endpoint.ex:1: ChallengeWeb.Endpoint.call/2
    (phoenix) lib/phoenix/endpoint/cowboy2_handler.ex:33: Phoenix.Endpoint.Cowboy2Handler.init/2
    (cowboy) /Users/machu/Localdev/challenge/deps/cowboy/src/cowboy_handler.erl:41: :cowboy_handler.execute/2
    (cowboy) /Users/machu/Localdev/challenge/deps/cowboy/src/cowboy_stream_h.erl:296: :cowboy_stream_h.execute/3
    (cowboy) /Users/machu/Localdev/challenge/deps/cowboy/src/cowboy_stream_h.erl:274: :cowboy_stream_h.request_process/3
    (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

I’d do Comments.create_comment_for(game, comment_params) and

def create_comment_for(game, params) do
  game
  |> Ecto.build_assoc(:comments)
  |> Comment.changeset(params)
  …
end
2 Likes

Thanks it’s working

1 Like