Reputation: 198
I've been stuck on this one problem for days now. This is my first Rails app, and I'm almost finished, only to be slowed considerably by this.
I read that using accepts_nested_attributes_for is the best solution to my problem of nesting Ingredients within a Recipe for use with forms, but so far I have had no luck. I have read everything I can find on the subject. The API says the model now has a (in my case) ingredients_attributes= method, which I do not yet see. I have attempted using update_attributes in the console with the hash
recipe.update_attrubutes {:ingredients_attributes=>[{:name=>"Test Ingredient"}]}
This comes back true, but shows no changes in the recipe object.
I have attempted many approaches, my first being just using fields_for inside of a forms_for inside of my View. Since this wasn't working and I was testing the code to no avail, I started looking deeper and the problem definitely goes deeper than the View.
Any help would be much appreciated. My code follows
Sorry about the confusing conversions between the db-styled names and the display-styled names. This is my best fix so far to maintain the both of them.
class Recipe < ActiveRecord::Base
DISH_TYPES={""=>"", "Breakfast"=>"breakfast", "Lunch"=>"lunch", "Soup"=>"soup", "Entree"=>"entree", "Desert"=>"desert"}
SEASONS={"Any Season"=>"any", "Fall"=>"fall", "Winter"=>"winter", "Spring"=>"spring", "Summer"=>"summer"}
DIETS={""=>"", "Vegan"=>"vegan", "Vegetarian"=>"vegetarian", "Omnivore"=>"omnivore"}
DISH_TYPES_R={""=>"", "breakfast"=>"Breakfast", "lunch"=>"Lunch", "soup"=>"Soup", "entree"=>"Entree", "desert"=>"Desert"}
SEASONS_R={"any"=>"Any Season", "fall"=>"Fall", "winter"=>"Winter", "spring"=>"Spring", "summer"=>"Summer"}
DIETS_R={""=>"", "vegan"=>"Vegan", "vegetarian"=>"Vegetarian", "omnivore"=>"Omnivore"}
attr_protected :user_id
# Do NOT include user_id in the attr_accessible method, to avoid
# the possibility of it being changed externally.
belongs_to :user
validates_presence_of :user
has_many :ingredients, dependent: :destroy # , inverse_of: :recipe
# Allows for forms to write attributes down the hierarchy.
accepts_nested_attributes_for :ingredients, allow_destroy: true , reject_if: lambda { |a| a[:content].blank? }
before_save do
# Lowercase and convert to strings all of the attributes
# that require a specific format, namely the values in the
# constant hashes above.
STRING_ATTRIBUTES.each do |s|
self.send("#{s}=".to_sym, self.send(s).downcase.to_s)
end
end
validates :user_id, presence: true
validates :name, presence: true,
length: { maximum: 64 } #,uniqueness: true
validates :dish_type, inclusion: { in: DISH_TYPES.values }
validates :season, inclusion: { in: SEASONS.values }
validates :diet, inclusion: { in: DIETS.values}
validates :directions, presence: true,
length: { maximum: 8192 }
validates_associated :ingredients
default_scope order: "recipes.created_at DESC"
def method_missing (method)
method = method.to_s
if method.slice!("display_")
if STRING_ATTRIBUTES.include?(method.to_sym)
hash_name = method.upcase + 'S_R'
Recipe.const_get(hash_name)[self.send(method.to_sym)]
else
method
end
else
method.class
end
end
private
STRING_ATTRIBUTES = [:dish_type, :season, :diet]
end
class Ingredient < ActiveRecord::Base
attr_protected :id, :recipe_id
belongs_to :recipe
validates_presence_of :name
#validates_presence_of :recipe
end
I've read that I shouldn't have to change anything in the controller. I have only added one line, so that the Ingredient form shows up in my view
class RecipesController < ApplicationController
before_filter :signed_in_user, only: [:create, :edit, :destroy]
def index
@recipes = Recipe.all
end
def new
if signed_in?
@recipe = current_user.recipes.build
@recipe.ingredients.build
else
flash[:error] = "First you have to register! Sign up here and start adding recipes ASAP."
redirect_to signup_path
end
end
def create
@new_recipe = current_user.recipes.build(params[:recipe])
if @new_recipe.save
flash[:success] = "You've successfully added #{@new_recipe.name}!"
redirect_to @new_recipe
else
redirect_to 'new'
end
end
def edit
@recipe = Recipe.find(params[:id])
end
def show
@recipe = Recipe.find(params[:id].to_i)
end
end
I don't believe there is any use for an Ingredient Controller, since the ingredients (for now) will only be accessed through their parent Recipe.
I will include the views upon request, but as I stated earlier, I don't believe it is that high-level.
Upvotes: 0
Views: 2407
Reputation: 456
attr_accessible
for Recipe
model and add there :ingredients_attributes
reject_if: lambda { |a| a[:content].blank? }
for ingrediens_attributes and you are setting it only with nameUpvotes: 1