Rails 4 form_for double nested comments - ruby-on-rails

First of all, I've found some very helpful answers already (see links below), but I'm still struggling to make this work. I haven't been programming for long, and this is definitely stretching what I feel comfortable claiming to understand.
I have three models. For example's sake, I'll call them Tree, Bark, Engraving (the comments). I'm trying to add a simple form partial for users to submit new comments (engravings).
The below setup is producing the error:
NoMethodError at /trees/1
undefined method `tree' for nil:NilClass
Controllers
class TreesController < ApplicationController
def show
#barks = Bark.where(tree_id: #tree.id)
#engraving= Engraving.new( :bark=> #bark)
end
end
class BarksController < ApplicationController
def show
#engravin= Engraving.new( :bark=> #bark)
#engravings = Engraving.where(bark_id: #bark.id)
end
end
class EngravingsController < ApplicationController
def show
#bark= Bark.find(params[:bark_id])
#engraving = Engraving.new(params[:engraving])
end
end
def create
#bark = Bark.find(params[:bark_id])
#engraving = Engraving.new(params[:engraving])
#engraving.user_id = current_user.id
respond_to do |format|
if #engraving.save
format.html { redirect_to tree_bark_path(#tree, #bark), notice: 'Comment was successfully created.' }
format.json { render action: 'show', status: :created, location: #engraving}
else
format.html { render action: 'new' }
format.json { render json: #engraving.errors, status: :unprocessable_entity }
end
end
end
Models
class Tree < ActiveRecord::Base
belongs_to :user
has_many :barks
has_many :engravings, through: :barks
end
class Bark< ActiveRecord::Base
belongs_to :user
belongs_to :tree
has_many :engravings
delegate :title, :link, :to => :tree, :prefix => true
def new_engraving_path
Rails.application.routes.url_helpers.new_tree_bark_engraving_path(book, self)
end
def serializable_hash(*args)
super.merge 'new_engraving_path' => new_engraving_path
end
end
class Engraving< ActiveRecord::Base
belongs_to :user
belongs_to :bark
delegate :tree, :to => :bark, :prefix => true
def to_edit_path
Rails.application.routes.url_helpers.edit_tree_bark_engraving_path bark_tree, bark, self
end
def to_path
Rails.application.routes.url_helpers.tree_bark_engraving_path bark_tree, bark, self
end
def serializable_hash(*args)
super.merge 'path' => to_path, 'edit_path' => to_edit_path
end
end
Calling the Form in a View
On the Tree show page, I have all the Bark populating there. Clicking on an Bark element opens a modal, where the Bark.content and Engravings are shown. Under the last Engraving, I want to have a text_area form to create a new Engraving, and add it to this "piece" of bark.
# trees\show.html.erb
<%= render #barks %>
# barks\_bark.html.erb
<%= render partial: 'barks/modal', locals: { bark: bark} %>
# barks\modal.html.erb
<% render partial: '/engravings/simpleForm', :collection => bark.engravings %>
# Form partial
<%= form_for #engraving, :url => (new_tree_bark_engraving_path(:tree_id => #bark.tree, :bark_id => #bark.id)) do |f| %>
<%= f.hidden_field :bark_id %>
<div class="field">
<%= f.text_area :content %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Routes
get "pages/home"
resources :trees do
collection do
get 'search'
post 'load_from_api'
end
resources :barks do
resources :engravings
end
end
resources :barks do
resources :engravings
end
root "trees#index"
Other Answer Sources
Rails Routing Error for nested form_for
Twice Nested Resource Form_For Issue
form_for with nested resources

Your error message NoMethodError at /trees/1 undefined method 'tree' for nil:NilClass means that somewhere you must be calling .tree on a nil object. The only place I can see that happening is in the form partial. I'm assuming that #bark.tree is failing.
From your question it is not clear which controller you are rendering the partial from, but the most likely cause is that #bark is not being passed into the form. Make sure that you are passing #bark into your partial, here is an example:
<%= render partial: "form", locals: {bark: #bark} %>

You're definining #barks in your trees#show action, but never #bark
def show
#barks = Bark.where(tree_id: #tree.id)
#engraving= Engraving.new( :bark=> #bark)
end
So #bark is nil.

Related

Submitting a form from one model's controller and view to a different model

I'm new to Rails and working on a sample app. The idea behind the app is that it's a computerized check-in sheet for kids to ride a school bus. There are four models: Family, Kid, SchoolRide, and HomeRide. For Family and Kid, I generated complete scaffolds, but for SchoolRide and HomeRide, they're just models with a boolean field each of whether the kid has checked in to the schoolbus in the morning or checked out in the afternoon when coming home.
I want to be able to have a user check in a kid from a form rendered on the kid show view, but I'm having trouble creating instances of my ride models from the kids controller. How do I set up the views, routing, and controllers? Where/how do I pass in the parameters to the ride models in the kids controller?
Here's my form rendered into the kid's show view. Currently, I'm getting a syntax error.
<%= form_with(model: #school_ride, remote: true), :url => school_rides_path, :html => { :method => :post } do |form| %>
<div><p>
<%= form.label :check_in %><br>
<%= form.check_box :check_in %><br>
</p>
</div>
<div>
<%= form.hidden_field :kid_id, value: #kid.id %>
</div>
<p>
<%= form.submit %>
</p>
<% end %>
Here're my models:
class Kid < ApplicationRecord
belongs_to :family
has_many :school_rides
has_many :home_rides
end
class HomeRide < ApplicationRecord
belongs_to :kid
end
class SchoolRide < ApplicationRecord
belongs_to :kid
end
Here are some relevant parts of my kids controller:
def show
#family = Family.all
#school_ride = SchoolRide.new
end
# GET /kids/new
def new
#kid = Kid.new
end
# GET /kids/1/edit
def edit
end
# POST /kids
# POST /kids.json
def create
#kid = Kid.new(kid_params)
respond_to do |format|
if #kid.save
format.html { redirect_to family_path(id: #kid.family_id), notice: 'Kid was successfully created.' }
format.json { render :show, status: :created, location: #kid }
else
format.html { render :new }
format.json { render json: #kid.errors, status: :unprocessable_entity }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_kid
#kid = Kid.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def kid_params
params.require(:kid).permit(:name, :birthdate, :grade, :family_id)
end
def school_ride_params
params.require(:school_ride).permit(:check_in)
end
Here's some of my routing:
resources :kids
resources :school_rides, only: [:new, :create]
Try this:
<%=form_for #school_rid, remote: true do |form| %>

Rails: Updating Nested attributes - undefined method `to_sym' for nil:NilClass

Edit: Added the update action, and on what line the error occurs
Model:
class Match < ActiveRecord::Base
has_and_belongs_to_many :teams
has_many :match_teams
has_many :teams, :through => :match_teams
accepts_nested_attributes_for :match_teams, :allow_destroy => true
end
Controller:
def new
#match = Match.new
#match_teams = 2.times do
#match.match_teams.build
end
respond_to do |format|
format.html # new.html.erb
format.json { render json: #match }
end
end
def update
#match = Match.find(params[:id])
respond_to do |format|
if #match.update_attributes(params[:match])
format.html { redirect_to #match, notice: 'Match was successfully updated.' }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: #match.errors, status: :unprocessable_entity }
end
end
end
Nested model:
class MatchTeam < ActiveRecord::Base
belongs_to :match
belongs_to :team
end
Association:
class Team < ActiveRecord::Base
has_and_belongs_to_many :matches
end
View:
<%= form_for(#match) do |f| %>
<%= f.fields_for :match_teams, #match_teams do |builder| %>
<%= builder.collection_select :team_id, Team.all, :id, :name, :include_blank => true %>
<% end %>
<% unless #match.new_record? %>
<div class="field">
<%= f.label :winning_team_id %><br />
<%= f.collection_select :winning_team_id, #match.teams, :id, :representation %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Params:
Processing by MatchesController#update as HTML
Parameters: {"utf8"=>"Ô£ô", "authenticity_token"=>"QIJChzkYOPZ1hxbzTZS8H3AXc7i
BzkKv3Z5daRmlOsQ=", "match"=>{"match_teams_attributes"=>{"0"=>{"team_id"=>"1", "
id"=>""}, "1"=>{"team_id"=>"3", "id"=>""}}, "winning_team_id"=>"3"}, "commit"=>"
Update Match", "id"=>"2"}
Creating a new match with 2 teams work fine, the edit view also shows the correct values, but the update action gives me this error.
undefined method `to_sym' for nil:NilClass
app/controllers/matches_controller.rb:65:in `block in update'
line 65: if #match.update_attributes(params[:match])
I've figured it out. I read that a join table like MatchTeams doesn't need an ID. I'm guessing this is true when not doing any nested forms. I redid my migration removing the exclusion of the id column, and now everything works fine. Don't we all love this stupid errors? :)
Without seeing the offending to_sym in your code, just know that the thing it's attached to has not been defined properly. If this is a variable such as #var.to_sym, you most likely:
Haven't set #var at all
Set it but it's returning nil because there are no matches (e.g. #var = #project.companies.first but #project has no companies tied to it).
You are missing a relevant bit of data in your params. If your to_sym is relying on data submitted through the form, it won't work if the user leaves out the bit of data you're assuming. In this case, you should test first to see if the data was entered before running .to_sym on it.

Rails 3 Nested Models unknown attribute Error

I have a model "Issue" and a nested Model "Relationship"
In the issue.rb I have mentioned:
has_many :relationships, :dependent => :destroy
accepts_nested_attributes_for :relationships, :allow_destroy => true
In relationship.rb I have mentioned:
belongs_to :issue
Following Ryan Bates Railcast#196 I have the following in my issues_controller:
relationship = #issue.relationships.build
However, I am encountering an error "unknown attribute: relationship"
Am I doing something incorrectly here? I do see the Relationships Attributes being passed to the server in the log however, this error does not let the create to be successful.
My expertise with rails is beginners level so kindly excuse me if I am asking a question which maybe deemed trivial.
Thanks for the help.
EDIT: The relevant Controller code:
#relationship = #issue.relationships.build
##relationship = Relationship.new(params[:relationship])
if #relationship.issue_id = ''
#relationship.issue_id = #issueid
end
if #relationship.cause_id = ''
#relationship.cause_id = #issueid
end
#relationship.save
redirect_to(:back, :notice => 'New Relationship was created')
What I see on the trace:
ActiveRecord::UnknownAttributeError in IssuesController#create
unknown attribute: relationship
Among the Issue parameters, I see the Relationship params being passed as expected:
"relationship"=>{"issue_id"=>"100",
"cause_id"=>""}
ANOTHER UPDATE
Posting the form_for code:
- form_for Issue.new do |f|
.field
= f.text_field :description, :class=>"formfield", :id=>"frm_descr"
.field
= f.hidden_field :wiki_url, :class=>"formfield", :id=>"frm_wiki_url"
.field
= f.hidden_field :short_url, :class=>"formfield", :id=>"frm_img_url"
.field
= f.hidden_field :title, :class=>"formfield", :id=>"frm_title"
= f.fields_for :relationship do |builder|
= builder.text_field :issue_id, :class=>"form_field", :id=>"frm_rel_issue_id", :value=>#issue.id
= builder.text_field :cause_id, :class=>"form_field", :id=>"frm_rel_cause_id"
.actions
= f.submit 'Create', :class=>"save_button", :name=>"save_issue_rel_button", :id=>"val_collector"
Change this line
= f.fields_for :relationship do |builder|
to this:
= f.fields_for :relationships do |builder|
Your issue has_many relationships - plural. That will give you the correct relationships_attributes parameters.
Here is the working skeleton code:
I created a new project and tried the combination of the other answers, and finally made it to work.
Here is my solution, after that are the things to watch out for. I am using different models so bear with me:
My models are: discussion has_many posts.
Discussion has no attributes.
Posts has content:text and discussion_id:integer.
Working Code
(model) discussion.rb
has_many :posts
accepts_nested_attributes_for :posts
(model) post.rb
belongs_to :discussion
routes.rb
resources :discussions do
resources :posts
end
(discussion view) _form.html.erb
<%= form_for(#discussion) do |f| %>
<%= f.fields_for :posts, #post do |p| %>
<%= p.text_area :content %>
<% end %>
<%= f.submit %>
<% end %>
(controller) discussions_controller.rb
def new
#discussion = Discussion.new
#post = #discussion.posts.build
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #discussion }
end
end
def create
#discussion = Discussion.new(params[:discussion])
respond_to do |format|
if #discussion.save
format.html { redirect_to(#discussion, :notice => 'Discussion was successfully created.') }
format.xml { render :xml => #discussion, :status => :created, :location => #discussion }
else
format.html { render :action => "new" }
format.xml { render :xml => #discussion.errors, :status => :unprocessable_entity }
end
end
end
Possible things that can go wrong
First, Thilo was right, I get unknown attribute: post if I do
# WRONG!
f.fields_for :post
Second, I have to have the #post instance variable in new action otherwise the post.context textarea will not show up.
# REQUIRED!
#post = #discussion.posts.build
Third, If I use the f.fields_for #post, the create action will complain unknown attribute: post too.
# WRONG!
f.fields_for #post do |p|
Use this instead:
# RIGHT!
f.fields_for :posts, #post do |p|
The End
So yeah, I wish we get to see more documentations on this (can't see any useful ones). For example I see some use of form_for [#discussion, #post] but I can never get it to work.
By using accepts_nested_attributes, you have created a setter method relationship_attributes=.
There are a couple of things I noticed that need to change.
You don't need to set
#relationship = #issue.relationships.build
Your form should be the following (you have f.fields_for :relationship)
= form_for #issue do |f|
# your issue fields here
= f.fields_for :relationships do |r|
# your relationship fields here
The beauty here is that you won't have to set any ids or anything.
I assume you are constructing the relationship in your controller and then trying to use it in the view. In order for this to be visible, you must make it an instance variable. All you need to do is throw an # symbol in from of the name of relationship, as you have done with #issue.
#relationship = #issue.relationships.build
Edit: due to further information provided by the OP after the original question was asked, this answer is now clearly not applicable.

Rails model/form layout question

I'm making a recipe-manager (who isn't as their first app?) in Rails, and here is my layout:
Ingredient belongs to Recipe
Recipe has many Ingredients
What is the best way to make a form that reflects this relationship? I was thinking an input that, when one is filled, creates another, so there is always 'one more' at the end of the form for ingredients.
Once I have the UI made, what would the structure of the model and controller look like? Right now I have the scaffolded controller create method:
def create
#recipe = Recipe.new(params[:recipe])
respond_to do |format|
if #recipe.save
format.html { redirect_to(recipes_path, :notice => 'You made a new recipe!') }
format.xml { render :xml => #recipe, :status => :created, :location => #recipe }
else
format.html { render :action => "new" }
format.xml { render :xml => #recipe.errors, :status => :unprocessable_entity }
end
end
end
Should the params[:recipe] just be a more deeply nested object/hash/dictionary, that contains an array of ingredients or something?
Thanks for any guidance here.
You should use accepts_nested_attributes here.
Some links:
API:
http://apidock.com/rails/ActiveRecord/NestedAttributes/ClassMethods/accepts_nested_attributes_for
Screencasts:
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
So your model will look like this
class Recipie < ActiveRecord::Base
has_many :ingredients
accepts_nested_attributes_for :ingridients, :allow_destroy => true
end
Views:
<%= form_for #recipe do |f| %>
... # reciepe fields
<%= f.fields_for :ingridients do |i| %>
... # your ingridients forms
<% end %>
...
<% end %>
And controller
def create
#recipe = Recipe.new(params[:recipe])
#recipe.save # some save processing
end
Just add ingredients by comma delimited.
This can be a text_field_tag because you will need to parse it and save each word spaced by a comma with a before save.
class Recipie < ActiveRecord::Base
has_many :ingredients
before_save :add_ingredients
attr_accessor :ingredients_to_parse #this will be the text_field_tag
def add_ingredients
#create an array of ingredients from #ingredients_to_parse
#then loop through that array i.e. you have your ingredients_array
ingredients_array.each do
Ingredient.create(:recipe => self, :other_params => 'stuff')
end
#there are a lot of ways, I just used create to show you how to add it
end
end
So then in your form just have that text_field_tag
<%= form_for(#recipe) do |f| %>
<% f.text_field :name %>
<% text_field_tag :ingredients_to_parse %>
<%= f.submit %>
<% end %>
Then you can add Javascript so that each time a comma is added in that text_field_tag you can just use some js to to so fancy stuff.
This way it will work when servers are slow, js is not working well, etc. It's always a good idea to get the HTML version going first too.
Good luck, let me know if you have questions/problems.

STI and form_for problem

I am using Single Table Inheritance for managing different types of projects.
Models:
class Project < ActiveRecord::Base
end
class SiteDesign < Project
end
class TechDesign < Project
end
Edit action from projects_controller:
def edit
#project = Project.find(params[:id])
end
View edit.html.erb:
<% form_for(#project, :url => {:controller => "projects",:action => "update"}) do |f| %>
...
<%= submit_tag 'Update' %>
<% end %>
Update action of projects_controller:
def update
#project = Project.find(params[:id])
respond_to do |format|
if #project.update_attributes(params[:project])
#project.type = params[:project][:type]
#project.save
flash[:notice] = 'Project was successfully updated.'
format.html { redirect_to(#project) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #project.errors, :status => :unprocessable_entity }
end
end
end
Then i do some edits of TechDesign entry on edit view and get error:
NoMethodError in ProjectsController#update
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.[]
In parametrs it is obvious that instead of project parameter name i have tech_design
Parameters:
{"commit"=>"Update",
"_method"=>"put",
"authenticity_token"=>"pd9Mf7VBw+dv9MGWphe6BYwGDRJHEJ1x0RrG9hzirs8=",
"id"=>"15",
"tech_design"=>{"name"=>"ech",
"concept"=>"efds",
"type"=>"TechDesign",
"client_id"=>"41",
"description"=>"tech"}}
How to fix it?
Here's the source of your problem. This is setting #project as an instance of a TechDesign object.
def edit
#project = Project.find(params[:id])
end
You can ensure things work the way you want by specifying :project for a name in the form_for call.
<% form_for(:project, #project, :url => {:controller => "projects",:action => "update"}) do |f| %>
...
<%= submit_tag 'Update' %>
<% end %>
For Rails 3
<% form_for(#project, :as => :project, :url => {:controller => "projects",:action => "update"}) do |f| %>
...
<%= submit_tag 'Update' %>
<% end %>
A random note: If you are using single table inheritance (STI) and forget to remove the initialize method from your subclass definitions you will get a similar "nil object when you didn't expect it" exception.
For example:
class Parent < ActiveRecord::Base
end
class Child < Parent
def initialize
# you MUST call *super* here or get rid of the initialize block
end
end
In my case, I used my IDE to create the child classes and the IDE created the initialize method. Took me forever to track down...
For Rails 4, I have confirmed the only thing that has seemed to work for me is to explicitly specify the URL and the AS parameters:
<% form_for(#project, as: :project, url: {controller: :projects, action: :update}) do |f| %>
...
<% end %>
Seems ugly to me!

Resources