Несколько belongs_to в приложении Phoenix

#elixir #phoenix-framework

#elixir #phoenix-framework

Вопрос:

Вот три модели, которые у меня есть в настоящее время

routine.ex

 defmodule DailyRoutine.Routine do
  use DailyRoutine.Web, :model

  schema "routines" do
    field :title, :string
    field :timeframe, :integer
    field :content, :string
    belongs_to :user, DailyRoutine.User
    has_many :comments, DailyRoutine.Comment 

    timestamps
  end

  @required_fields ~w(title timeframe content)
  @optional_fields ~w()
  # rest of the code goes here
end
 

пользователь.бывший

 defmodule DailyRoutine.User do
  use DailyRoutine.Web, :model

  schema "users" do
    field :name, :string
    field :username, :string
    field :password, :string, virtual: true
    field :password_hash, :string
    has_many :routines, DailyRoutine.Routine
    has_many :comments, DailyRoutine.Comment 

    timestamps
  end

  @required_fields ~w(name username)
  @optional_fields ~w()
  # rest of the model code goes here    
end
 

комментарий.пример

 defmodule DailyRoutine.Comment do
  use DailyRoutine.Web, :model

  schema "routines" do
    field :content, :string
    belongs_to :user, DailyRoutine.User
    belongs_to :routine, DailyRoutine.Routine

    timestamps
  end

  @required_fields ~w(user_id routine_id content)
  @optional_fields ~w()
  #rest of the code goes here
end
 

Теперь проблема в том, что я не смог найти способ в контроллере сделать несколько build_assoc и связать их вместе, чтобы создать правильный набор изменений.

Вот мой контроллер:

comment_controller.ex

 defmodule DailyRoutine.CommentController do
  use DailyRoutine.Web, :controller
  alias DailyRoutine.Comment

  def create(conn, %{"routine_id" => routine_id, "comment" => comment_params}, user) do
    routine = Repo.get!(DailyRoutine.Routine, routine_id) |> Repo.preload(:user)

    changeset = 
      user
      |> build_assoc(:comments)
      |> Repo.preload(:routine)
      |> Comment.changeset(comment_params)
      |> Ecto.Changeset.put_assoc(:routine, routine)

    case Repo.insert(changeset) do
      {:ok, comment} ->
        conn
        |> put_flash(:info, "Your comment was successfully posted!")
        |> redirect(to: routine_path(conn, :show, routine_id))
      {:error, changeset} ->
        redirect(conn, to: routine_path(conn, :show, routine_id))
    end
  end

  def action(conn, _) do
    apply(__MODULE__, action_name(conn), [conn, conn.params, conn.assigns.current_user])
  end
end
 

То, что у меня есть до сих пор, не работает, и я пробовал несколько вещей, включая создание структуры вручную с конкретными отношениями id и т. Д.

Большое спасибо!

Ответ №1:

Я бы просто ввел идентификаторы вручную в структуру и передал их Comment.changeset/2 для этого вместо того, чтобы пытаться использовать build_assoc , поскольку у вас уже есть routine и он user загружен:

 changeset =
  %Comment{user_id: user.id, routine_id: routine_id}
  |> Comment.changeset(comment_params)
case Repo.insert(changeset) do
  ...
end
 

Поскольку у вас есть routine_id и user_id в @required_fields , вы также можете поместить эти идентификаторы в параметры, которые вы передаете Comment.changeset/2 :

 comment_params =
  comment_params
  |> Map.put("user_id", user.id)
  |> Map.put("routine_id", routine_id)
changeset = Comment.changeset(%Comment{}, comment_params)
 

Комментарии:

1. Я пробовал что-то подобное, но по какой-то причине я продолжаю получать эту ошибку: column "routine_id" of relation "routines" does not exist даже если у меня есть настройка отношений и столбцы в БД.

2. Я на самом деле только что сделал IEx , и набор изменений действителен, и там все хорошо, но, похоже, проблема в column_id relation ошибке, которую я получаю

3. Когда вы получаете эту ошибку? После Repo.insert! ? Что произойдет, если вы попытаетесь вставить набор изменений из iex ?

4. Только что понял, что было не так: у вас есть schema "routines" do в Comment модели.