It seems like you are passing an empty attrs map in Blog.change_post(%Post{}) (the contents of the change_post/1 might be helpful), and in your changeset/2 you expect it to have a :user.
To avoid the error, try replacing your current change_post/1 with
def change_post(post) do
Ecto.Changeset.change(post)
end
Since you are using Blog context in your code, I would suggest putting put_assoc in your context instead of messing your changeset/2, for example, you can define change_post/2 as bellow:
def change_post(%User{} = user, %Post{} = post) do
Post.changeset(post, %{})
|> put_assoc(:user, user)
end
Then in your new action:
def new(conn, _) do
changeset = Blog.change_post(conn.assigns.current_user, %Post{})
render(conn, "new.html", changeset: changeset)
end
I try to follow what you suggested but error occurs on the edit action./
So here’s what I’ve done.
I change the change_post to this:
def change_post(%User{} = user, %Post{} = post) do
Post.changeset(post, %{})
|> put_assoc(:user, user)
end
then use it in the edit controller action:
def edit(conn, %{"id" => id}) do
post = Blog.get_post!(id)
changeset = Blog.change_post(conn.assigns.current_user, post)
render(conn, "edit.html", post: post, changeset: changeset)
end
but I get this error instead
you have set that the relation :user of … has :on_replace set to :update but you are giving it a struct/ changeset to put_assoc/put_change.`
here’s my post schema:
schema "posts" do
field :content, :string
field :is_published, :boolean, default: false
field :slug, :string
field :title, :string
belongs_to :user, Pluma.Accounts.User, on_replace: :update
timestamps()
end
solution given by @idi527 works without this issues.
Depends on your use case, if all you need is to get a changeset without any transformations (which is usually the case for new forms), then yes, change/1 is ok.
I am not sure what is it that you are trying to achieve.
put_assoc is intended to be used when working with the parent and child associations at once.
So you should preload all the child records first and then call the put_assoc function. It will compare the preloaded records with the new records using the id as key and will determine what to do with each record. The on replace function is triggered when a id is missing in the new records. Ecto assumes you deleted the record and acts accordingly.
Please review the Ecto’s documentation: put_assoc/4
The documentation covers different use cases that might be suitable for you needs.
First, I don’t think on_replace: :update in your posts schema is suitable here. From the documentation we know:
:update - updates the association, available only for has_one and belongs_to . This option will update all the fields given to the changeset including the id for the association
Do you really want to update the user when inserting your new post every time? I think you just want to associate your new post to an existing user.