Search code examples
elixirphoenix-frameworkecto

How can I pass association to changeset in Ecto?


I have a pretty simple Comment model:

  schema "comments" do
    field :content, :string
    belongs_to :user, MyApp.User
    timestamps
  end

I want to create a new record and I want changeset that validates that user exists. How can I do that?

My ecto version is 1.1.8.


Solution

  • The correct way to do this is to let the database handle data integrity. Writing a check for this in your application is prone to race conditions. What if a User is deleted between your validation and Comment insertion? What if a User with any existing Comments is deleted? Handling this in the database will make sure you never have invalid data in your database.

    First, make sure that the user_id field is set to references(:users):

    create table(:comments) do
      ...
      # Check out the various :on_delete options you can pass to here: https://hexdocs.pm/ecto/Ecto.Migration.html#references/2
      add :user_id, references(:users, on_delete: :nilify_all)
    end
    

    then, in your changeset function, after piping to cast, add this:

    |> Ecto.Changeset.assoc_constraint(:user)
    

    This will convert errors related to user_id returned by the database into normal Changeset errors. You can read more about Ecto.Changeset.assoc_constraint/3 here. Also see Ecto.Changeset.html#foreign_key_constraint/3.