It’s been brought up several times that casting foreign keys is a bad practice.
However, if you want cast_assoc to work properly, you have to have the parent id listed in the child cast fields, otherwise the child associations don’t get their parent id. Am I correct about that?
Just wondering if everything was correct, and if there are other better ways of handling it.
Can you post some code of how you’re currently doing it? I believe cast_assoc should be called from the parent changeset, which means it does have the parent ID and will be able to assign it to the child. For instance,
%{child_data: %{some: data}} = changes
def changset_fun_of_parent(parent, changes) do
parent
|> cast(params, [ ])
|> cast_assoc(:child)
end
a Post has many comments, it obviously belongs to a User but not important here
a Comment has some text body, belongs to a Postand belongs to a User
Let’s say I follow the “no cast fk” rule
I don’t cast either post_id or user_id in Comment's changeset function
And I have cast_assoc(:comments) in Post's changeset function
When I create a Post with changeset(%Post{user_id: ...}, %{"comments" => [%{"body" => "...", "user_id" => user_id}]}
I would get some error like this for the comment we are trying to create alongside the post: errors: [post_id: {"can't be blank", [validation: :required]}, user_id: {"can't be blank", [validation: :required]}]
The user_id part I can understand, I probably have to cast_assoc(:user) on Comment as well.
The post_id part I don’t understand, does cast_assoc not pass parent id properly via the Struct but via the params?
What’s the proper way of doing this?
TL;DR - if the foreign key field is meant to be set programatically, then don’t add a validation.
I don’t have enough context to answer for sure but it is worth pointing out that data in Ecto is validated upfront on all entities. So if you are expecting user_id to be set and that is set through an association, then the validation will never pass.
However, if user_id is meant to be set programatically, then it should not have a validation in the first place. A validation is meant to be reported to users of your application. If the user gets a “post_id is blank” error and the field is set programatically, how can they even fix the error?
In those cases, it is best to make sure the operation fails altogether (by using DB constraints for example), so you detect such bugs early.
In those cases, it is best to make sure the operation fails altogether (by using DB constraints for example), so you detect such bugs early.
One might say that to “make sure the operation fails altogether”, you might perform some kind of validation, so you don’t have to go to the database to get an error message.
But I’m mainly just here to put a +1 on finding it confusing.