views:

38

answers:

1

I have a Recipe model which has_many Ingredients (which in turn belongs_to Recipe). I want Ingredient to be existent dependent on Recipe; an Ingredient should never exist without a Recipe.

I'm trying to enforce the presence of a valid Recipe ID in the Ingredient. I've been doing this with a validates :recipe, :presence => true (Rails 3) statement in Ingredient. This works fine if I save the Recipe before adding an Ingredient to it's ingredients collection. However, if I don't have explicit control over the saving (such as when I'm creating a Recipe and its Ingredients from a nested form) then I get an error:

Ingredients recipe can't be blank

I can get around this simply by dropping the presence validation on Ingredient.recipe. However, I don't particularly like this, as it means I'm working without a safety net.

What is the best way to enforce existence-dependence in Rails?

Things I'm considering (please comment on the wisdom of each):

  • Adding a not-null constraint on the ingredients.recipe_id database column, and letting the database do the checking for me.
  • A custom validation that somehow checks whether the Ingredient is in an unsaved recipe's ingredient collection (and thus can't have a recipe_id but is still considered valid).
+2  A: 

Take a look at this:

https://rails.lighthouseapp.com/projects/8994/tickets/2815-nested-models-build-should-directly-assign-the-parent

In the past I think I've added a :on => :update option to the validator, helps tighten the net a little. I think the foreign key restriction in the DB is a good idea, although I rarely use it : (

tsdbrown
Interesting ticket. Contains the answers the OP needs. As a good practice, i always make sure that my database has all the necessary constraints (validations). Just in case someone comes along with some script to fill my db, or with some SQL-editor and starts messing around. It makes sure that the meaning of the datamodel in the database is clear by itself.
nathanvda
Yeah that's a sound move. I need to kick myself to ensure I always do it.
tsdbrown