Reputation: 193
I have Memberships, Lists, Items and Memories defined as follows :
schema "lists" do
has_many :itemlists, Learnit.Itemlist, on_delete: :delete_all
many_to_many :items, Learnit.Item, join_through: Learnit.Itemlist
has_many :memberships, Learnit.Membership, on_delete: :delete_all
end
schema "memberships" do
belongs_to :list, Learnit.List
has_many :memorys, Learnit.Memory, on_delete: :delete_all
end
schema "itemlists" do
belongs_to :item, Learnit.Item
belongs_to :list, Learnit.List
end
schema "items" do
has_many :itemlists, Learnit.Itemlist
many_to_many :lists, Learnit.List, join_through: Learnit.Itemlist
has_many :memorys, Learnit.Memory
end
schema "memorys" do
belongs_to :membership, Learnit.Membership
belongs_to :item, Learnit.Item
end
My Membership's model
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:user_id, :list_id])
|> validate_required([:user_id, :list_id])
|> cast_assoc(:memorys)
end
My Memory's model
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:status, :membership_id, :item_id])
|> foreign_key_constraint([:membership_id, :item_id])
|> unique_constraint(:membership_id_item_id)
|> validate_required([:membership_id, :item_id])
end
I try to create a new membership and generate his memories associated with one query. I need to get first Items that are associated with the Membership through its list. Then my plan is to use Membership's cast_assoc property to save the whole in Repo.
How can I load these items in the Membership's changeset? Items are already loaded correctly, I get a Map.put/4 Undefined...
def create(conn, %{"membership" => membership_params}) do
memories = %{}
list =
List
|> Repo.get!(membership_params["list_id"])
|> Repo.preload(:items)
|> Map.get(:items) # Get the list of items
|> Enum.map(&load_items(&1, memories)) # Loop through the list to get each item
IO.inspect(memories)
Map.put(membership_params, :memorys, memories)
membership_with_memories = Membership.changeset(%Membership{}, membership_params)
case Repo.insert(membership_with_memories) do
{:ok, _} ->
conn
|> put_flash(:info, "Membership created successfully.")
|> redirect(to: list_path(conn, :index))
{:error, membership_with_memories} ->
Logger.debug("Membership : failed to save membership")
conn
|> put_flash(:alert, "Membership was not created.")
|> redirect(to: topic_path(conn, :index))
end
end
defp load_items(item, memories) do
item
|> Map.put(memories, :item, item) # Add item to the hash
end
Upvotes: 1
Views: 535
Reputation: 193
Just found the problem in my Memory's model. I don't really get why the validate_required blocks the cast_assoc, but it is fine for me...
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:status, :membership_id, :item_id])
#|> foreign_key_constraint([:membership_id, :item_id]) # Dont use that !
|> unique_constraint(:membership_id_item_id) # Dont forget to put constraint on table too
|> validate_required([:item_id]) # Cannot use :membership_id with cast_assoc
end
Upvotes: 0
Reputation: 193
I found my way to load the params correctly, but still I cant find the way to save it in repo (I get a "cannot convert the given list to a string").
%{"list_id" => "1", "memorys" => [%{"item_id" => "1"}, %{"item_id" => "2"}, %{"item_id" => "3"}], "user_id" => "1"}
def create(conn, %{"membership" => params}) do
memories = []
params =
List
|> Repo.get!(params["list_id"])
|> Repo.preload(:items)
|> Map.get(:items) # Get the list of items
|> Enum.map(&add_items(&1, memories)) # Loop through the list to get list of item ids
|> (&Map.put(params, "memorys", &1)).() # Add the list to membership params
|> IO.inspect()
membership_with_memories = Membership.changeset(%Membership{}, params)
case Repo.insert(membership_with_memories) do
{:ok, _} ->
conn
|> put_flash(:info, "Membership created successfully.")
|> redirect(to: list_path(conn, :index))
{:error, membership_with_memories} ->
Logger.debug("Membership : failed to save membership")
conn
|> put_flash(:error, "Membership was not created.")
|> redirect(to: list_path(conn, :index))
end
end
defp add_items(item, memories) do
memories ++ %{"item_id" => Kernel.inspect(item.id)} # Make sure id is a string
end
Upvotes: 1