Reputation: 2665
I'm struggling to get nested attributes down. Working off of Railscast 196, I tried to setup my own app that does basic nesting. Users can create scavenger hunts. Each hunt consists of a series of tasks (that can belong to any hunt, not just one). I got a little help here and tried to learn from a post with a similar issue, but I'm still stuck. I've been hacking around for hours and I've hit a brick wall.
class HuntsController < ApplicationController
def index
@title = "All Hunts"
@hunts = Hunt.paginate(:page => params[:page])
end
def show
@hunt = Hunt.find(params[:id])
@title = @hunt.name
@tasks = @hunst.tasks.paginate(:page => params[:page])
end
def new
if current_user?(nil) then
redirect_to signin_path
else
@hunt = Hunt.new
@title = "New Hunt"
3.times do
#hunt = @hunt.tasks.build
#hunt = @hunt.hunt_tasks.build
hunt = @hunt.hunt_tasks.build.build_task
end
end
end
def create
@hunt = Hunt.new(params[:hunt])
if @hunt.save
flash[:success] = "Hunt created!"
redirect_to hunts_path
else
@title = "New Hunt"
render 'new'
end
end
....
end
With this code, when I try and create a new hunt, I'm told that there's no method "build_task" (it's undefined). So when I remove that line and use the second bit of code that's commented out above, I get the error below.
NoMethodError in Hunts#new
Showing /Users/bendowney/Sites/MyChi/app/views/shared/_error_messages.html.erb where line #1 raised:
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.errors
Extracted source (around line #1):
1: <% if object.errors.any? %>
2: <div id="error_explanation">
3: <h2><%= pluralize(object.errors.count, "error") %>
4: prohibited this <%= object.class.to_s.underscore.humanize.downcase %>
Trace of template inclusion: app/views/tasks/_fields.html.erb, app/views/hunts/_fields.html.erb, app/views/hunts/new.html.erb
And when I use the first bit of code that's commented out in the hunt controller, then I get an error telling me that my 'new' method has an unintialized constant:
NameError in HuntsController#new
uninitialized constant Hunt::Tasks
I'm at my wit's end. Any suggestions on what exactly I'm doing wrong? Or a strategy Here are my models:
class Hunt < ActiveRecord::Base
has_many :hunt_tasks
has_many :tasks, :through => :hunt_tasks #, :foreign_key => :hunt_id
attr_accessible :name
validates :name, :presence => true,
:length => { :maximum => 50 } ,
:uniqueness => { :case_sensitive => false }
end
class Task < ActiveRecord::Base
has_many :hunt_tasks
has_many :hunts, :through => :hunt_tasks#, :foreign_key => :hunt_id
attr_accessible :name
validates :name, :presence => true,
:length => { :maximum => 50 } ,
:uniqueness => { :case_sensitive => false }
end
class HuntTask < ActiveRecord::Base
belongs_to :hunts # the id for the association is in this table
belongs_to :tasks
end
Upvotes: 1
Views: 4060
Reputation: 361
When you create an association between 2 of your models, you add functionality to them, depending on how you define your relationship. Each type kinda adds different functions to your model.
I really recommend reading this guide -> http://guides.rubyonrails.org/association_basics.html
Here you can see which functions get added by each different type of association. http://guides.rubyonrails.org/association_basics.html#detailed-association-reference
If I do a small sample program like...
class HuntsController < ApplicationController
# GET /hunts
# GET /hunts.json
def index
@hunts = Hunt.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: @hunts }
end
end
# GET /hunts/1
# GET /hunts/1.json
def show
@hunt = Hunt.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: @hunt }
end
end
# GET /hunts/new
# GET /hunts/new.json
def new
@hunt = Hunt.new
3.times do |i|
t = @hunt.hunt_tasks.build
t.name = "task-#{i}"
end
respond_to do |format|
format.html # new.html.erb
format.json { render json: @hunt }
end
end
# GET /hunts/1/edit
def edit
@hunt = Hunt.find(params[:id])
end
# POST /hunts
# POST /hunts.json
def create
@hunt = Hunt.new(params[:hunt])
respond_to do |format|
if @hunt.save
format.html { redirect_to @hunt, notice: 'Hunt was successfully created.' }
format.json { render json: @hunt, status: :created, location: @hunt }
else
format.html { render action: "new" }
format.json { render json: @hunt.errors, status: :unprocessable_entity }
end
end
end
# PUT /hunts/1
# PUT /hunts/1.json
def update
@hunt = Hunt.find(params[:id])
respond_to do |format|
if @hunt.update_attributes(params[:hunt])
format.html { redirect_to @hunt, notice: 'Hunt was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @hunt.errors, status: :unprocessable_entity }
end
end
end
# DELETE /hunts/1
# DELETE /hunts/1.json
def destroy
@hunt = Hunt.find(params[:id])
@hunt.destroy
respond_to do |format|
format.html { redirect_to hunts_url }
format.json { head :no_content }
end
end
end
and this model-relation
class Hunt < ActiveRecord::Base
has_many :hunt_tasks
end
class HuntTask < ActiveRecord::Base
belongs_to :hunt
end
and add this snippet somewhere in views/hunts/_form.html
<% @hunt.hunt_tasks.each do |t| %>
<li><%= t.name %></li>
<% end %>
I get regular output, seeing that the 3 tasks were created.
Upvotes: 1
Reputation: 7304
The immediate error you are seeing is in app/views/shared/_error_messages.html.erb. object is not defined, You probably need to find where that partial is called. Find:
render :partial=>"/shared/error"
replace it with
render :partial=>"/shared/error", :locals=>{:object=>@hunt}
If you find it in app/views/hunts somewhere, if you find in in app/views/tasks, replace @hunt with @task
That will at least show you what the real error is.
Upvotes: 0
Reputation: 361
have you tried
hunttask = @hunt.build_hunt_task
in the HuntsController new action?
http://guides.rubyonrails.org/association_basics.html#detailed-association-reference
Upvotes: 1