Unique validations on embedded schemas

I have an embedded schema that maps to a form. The embedded schema is meant to be mapped to two other database-backed schemas if the data is valid. One of the validations that need to be performed is a uniqueness validation against one of the fields. I’m not sure what the best approach is to perform the uniqueness validation.

1 Like

If you ask me that uniqueness validation can only happen in the DB on the actual insert, so you should look at using a Multi with the two DB backed schemas https://hexdocs.pm/ecto/Ecto.Multi.html

1 Like

Agree with @outlog, you should add the unique_constraint in a changeset against the database-backed schema. You can do all of your normal validations in a changeset for your embedded schema then use an Ecto.Multi to map that incoming form data to the inserts/updates against your actual DB tables.

This post by Mohamad El-Husseini and the comment at the bottom by Michał Muskała is pure gold for this problem: https://medium.com/@abitdodgy/building-many-to-many-associations-with-embedded-schemas-in-ecto-and-phoenix-e420abc4c6ea. Also the conversation between the author and José was helpful to me: https://groups.google.com/forum/#!topic/elixir-ecto/_QHS-IY-sQY. One key thing from the post is you should use his copy_errors function to get the constraint error from the DB and return to the caller as if it was generated by the embedded schema validations.

Finally, although not related to your constraint question Chris McCord shows a related data-driven schemas pattern at 16:55 in the video from Lonestar Elixir last year: https://www.youtube.com/watch?v=tMO28ar0lW8. I wanted to add that since the pattern in that example differs slightly from the other posts and could help to see.

Wish there were more examples out in the wild because I’m thinking this is the future go-to pattern for form data involving more than simple single table CRUD insert/update operations in contexts in the future. It really is great to be able to decouple the UI concerns (forms) from your DB structure and commit everything in transactions using Ecto.Multi.

3 Likes

Thanks for your input!

At the moment I’m more concerned with performing an unsafe_validate_unique/4 on the field. The constraint will come after I’ve figured out the validation piece. The Ecto.Multi advice will probably be what I need when I’m ready to commit the data.

I asked a related question on the ecto-talk Google Group and @josevalim recommended rolling my own uniqueness validation for this circumstance.

@tme_317 (or anyone) what would you do if the form interacts with multiple contexts? Would the context function return a multi that you could then compose?

It is worth saying that using Ecto.Multi across contexts violates the context boundaries because you are assuming the other context is using repo. This is fine though, my point is that you are then coupling a context with another. So I would establish a relationship between the context (one context depends on another) and have that a single context define the multi and inside the context you call the other contexts to define your multi.

1 Like

Thanks that makes a lot of sense. I think if I were to design my application again I would draw different context boundaries.