1
votes

Working through a sample guide. What's detailed in the chapter doesn't work in my app. Pretty simple stuff, it would seem. I've got a Video model:

defmodule Rumbl.Video do
  use Rumbl.Web, :model

  schema "videos" do
    field :url, :string
    field :title, :string
    field :description, :string
    belongs_to :user, Rumbl.User
    belongs_to :category, Rumbl.Category

    timestamps()
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.
  """
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:url, :title, :description])
    |> validate_required([:url, :title])
    |> assoc_constraint(:category)
  end
end

I've also got a Category model:

defmodule Rumbl.Category do
  use Rumbl.Web, :model

  schema "categories" do
    field :name, :string

    timestamps()
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.
  """
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:name])
    |> validate_required([:name])
  end

  def alphabetical(query) do
    from c in query, order_by: c.name
  end

  def names_and_ids(query) do
    from c in query, select: {c.name, c.id}
  end
end

In an IEX session, I load a Video record as so:

iex(21)> video = Repo.one(from v in Video, limit: 1)
[debug] QUERY OK source="videos" db=16.0ms
SELECT v0."id", v0."url", v0."title", v0."description", v0."user_id", v0."category_id", v0."inserted_at", v0."updated_at" FROM "videos" AS v0 LIMIT 1 []
%Rumbl.Video{__meta__: #Ecto.Schema.Metadata<:loaded, "videos">,
 category: #Ecto.Association.NotLoaded<association :category is not loaded>,
 category_id: nil, description: "test1", id: 2,
 inserted_at: #Ecto.DateTime<2017-01-02 06:50:26>, title: "test1",
 updated_at: #Ecto.DateTime<2017-01-02 06:50:26>, url: "test1 video.com",
 user: #Ecto.Association.NotLoaded<association :user is not loaded>,
 user_id: 10}

I get why the category and user associations are not loaded. I didn't preload the user and there isn't a category association yet to load.

Either way, I've got my video in memory:

iex(22)> v.id
2

Now I load my category:

iex(23)> category = Repo.get_by Category, name: "Comedy"
[debug] QUERY OK source="categories" db=0.0ms
SELECT c0."id", c0."name", c0."inserted_at", c0."updated_at" FROM "categories" AS c0 WHERE (c0."name" = $1) ["Comedy"]
%Rumbl.Category{__meta__: #Ecto.Schema.Metadata<:loaded, "categories">, id: 4,
 inserted_at: #Ecto.DateTime<2017-01-07 07:03:00>, name: "Comedy",
 updated_at: #Ecto.DateTime<2017-01-07 07:03:00>}

Just to prove that I have it:

iex(24)> category.id
4

Now I try to associate the video with the category:

iex(25)> changeset = Video.changeset(video, %{category_id: category.id})
#Ecto.Changeset<action: nil, changes: %{}, errors: [], data: #Rumbl.Video<>,
 valid?: true>
iex(26)> Repo.update(changeset)
{:ok,
 %Rumbl.Video{__meta__: #Ecto.Schema.Metadata<:loaded, "videos">,
  category: #Ecto.Association.NotLoaded<association :category is not loaded>,
  category_id: nil, description: "test1", id: 2,
  inserted_at: #Ecto.DateTime<2017-01-02 06:50:26>, title: "test1",
  updated_at: #Ecto.DateTime<2017-01-02 06:50:26>, url: "test1 video.com",
  user: #Ecto.Association.NotLoaded<association :user is not loaded>,
  user_id: 10}}

I don't understand why there aren't any changes in the changeset. This is how the guide instructs to do an association. Am I missing something?

Thanks, John

1

1 Answers

4
votes

I figured it out. I needed to add the category_id to the list of params in the Video model:

def changeset(struct, params \\ %{}) do
  struct
  |> cast(params, [:url, :title, :description, :category_id])
  |> validate_required([:url, :title])
  |> assoc_constraint(:category)
end