Hello. I do have some working code but I would like to know if there is a better option.
I have a schema “countries”
@primary_key {:code, :string, []}
@derive {Phoenix.Param, key: :code}
schema "countries" do
field :name, :string
end
and a schema “user_profiles”
schema "user_profiles" do
# ... things
belongs_to :country, Country, references: :code,
foreign_key: :country_code, type: :string
end
Now when I want to create a new user profile I do
country_struct = Repo.get(Country, country)
profile =
%UserProfile{}
|> UserProfile.registration_changeset(params)
|> put_assoc(:country, country_struct, required: true)
|> IO.inspect
user =
User.registration_changeset(%User{}, params, key)
|> Changeset.put_assoc(:user_profile,
The country
variable is a string, the two digits country code. For example “GR”. If the user submits a country code that doesn’t exist in the database, the country_struct
variable becomes nil
.
When I pass that variable to put_assoc/3
it proceeds happily, even if the assoc is nil
. The changeset remains valid. This is how put_assoc/3
is supposed to work.
Looking from the Ecto.Changeset source, passing required: true
in the options doesn’t seem to have any effect.
What I did to fix this is to change the line that gets the country from the DB to
country_struct = Repo.get(Country, country) || :error
So put_assoc/3
receives the %Country{}
struct or :error
and the changeset becomes invalid in case no country with the given id is found in the database.
Is there any better way to do this according to Ecto design?