I'm coming to the Phoenix framework from Rails. So far it's been a fairly easy transition. Phoenix is newer, though, and I'm having trouble finding some specific information:
I'm using my Phoenix app as an API service layer.
I want my UI form (and incoming curl requests) to use a virtual field
to find an associated parent model, and fill the child model's changeset
with the appropriate attribute. So far, so good:
in my child model:
schema "child" do
field :parent_name, :string, virtual: true
belongs_to :parent, MyApp.Parent
end
...
before_insert :find_and_fill_parent
def find_and_fill_parent(changeset) do
parent = MyApp.Repo.get_by(
MyApp.Parent,
parent_name: changeset.changes.parent_name
)
changeset
|> Ecto.Changeset.put_change(:parent_id, parent.id)
end
This is where I'm stuck. I do not want to allow the child to be created without a parent. How and where do I nil
check the parent model? Everything I've tried has either blocked or allowed creation unconditionally, despite conditional statements.
It seems like I need to preload the parent model before the check, since Phoenix is designed to prevent people like me from abusing lazy load. Unfortunately, I don't know what the proper load pattern is in general, so I'm not sure how to apply it here. (I'm using MySQL, if that's relevant)
Hints and tips about where to look and what to look at to help me figure this out are appreciated! Thanks!
---EDIT---
Per @Gazler 's advice I have made sure that my child model migration has a reference:
create table(:child) do
add :parent_id, references(:parent)
end
I'm still a little lost -- I want to find the parent
by the parent field parent_name
("Jane Doe"
), make sure the parent model exists, and associate the child using parent_id
(1
). I'm not sure how to trigger these actions around using a virtual field.
So, I'm not sure how to structure finding the parent, building the association, and checking the foreign key validation, since the foreign key will never exist in the original params. Thoughts?
Thanks so much.
---RESOLVED---
Using @Gazler 's updated answer, I can successfully nil check my parent model in the child controller without a virtual attribute or before_insert.
def create(conn, %{"post" => post_params}) do
user = Repo.get_by(User, %{name: post_params["name"]})
if is_nil(user) do
changeset = Post.changeset(%Post{}, post_params)
else
changeset = build(user, :posts) |> Post.changeset(post_params)
end
etc
end
this validates the incoming parameters exactly like i need! thanks @Gazler!