Rails 3 Nested Models unknown attribute Error - ruby-on-rails

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.

Related

Rails passing nested attributes but not saving associated object

OK so I've been at this for 6 plus hours and scoured every variation on this question, but nothing I do works!
I'm building a fairly simple family tree app for a friend. There are two models in question, Person and Relationship. Whenever you build a new Person (after the very first one which is handled separately), it should also build a Relationship which is essentially a join table that has a person_1 attribute, a person_2 attribute, and a relationship_type attribute such as "mother".
When I build a new person, that person is saved to the database just fine, but the associated relationship is not. I can see that all of the necessary params are being passed in, and I've read as much as I can about nested params, but no dice.
My code is a big old mess and I'm a beginner so please ignore any unrelated code weirdness.
My Person model (this centers around the out_relationships resource)
class Person < ApplicationRecord
has_many :out_relationships, :class_name => "Relationship", :foreign_key => "person_1_id"
has_many :in_relationships, :class_name => "Relationship", :foreign_key => "person_2_id"
belongs_to :user, optional: true
accepts_nested_attributes_for :out_relationships
end
My Relationship model:
class Relationship < ApplicationRecord
belongs_to :person_1, :class_name => "Person"
belongs_to :person_2, :class_name => "Person"
end
My form:
<%= form_for #person, :url => create_relative_path, html:{method:'post'} do |form| %>
<%= fields_for :out_relationships do |builder| %>
<div class="form-group">
<%= builder.label :relationship_type, "Relationship to #{#root_person.first_name}" %>
<%= builder.text_field :relationship_type, class: "form-control" %>
<%= builder.hidden_field :person_1, value: #person_1.id %>
<%= builder.hidden_field :person_2, value: #person_1.id %>
</div>
<% end %>
I set person_1 and person_2 to the same value just as a test. That's kind of unrelated to the problem, I think, and shouldn't affect what's happening. Anyways...
My controller:
def new
#root_person = Person.find(params[:id])
#person = Person.new
#user = current_user
#root_person.out_relationships.build
end
# GET /people/1/edit
def edit
end
def create
#root_person = Person.find(params[:id])
#person = Person.new(person_params)
#user = current_user
respond_to do |format|
if #person.save
format.html { redirect_to #person, notice: 'Person was successfully created.' }
format.json { render :show, status: :created, location: #person }
else
format.html { render :new }
format.json { render json: #person.errors, status: :unprocessable_entity }
end
end
end
def person_params
params.require(:person).permit(:first_name, :middle_name, :last_name, :birth_date, :birth_city,
:birth_country, :current_city, :current_country, :profession, :maiden_name, :marital_status, :patel_type,
out_relationships_attributes: [:person_1, :person_2, :relationship_type])
end
Anyways, that's more of a mess than I even thought, thanks to anyone still reading, would love a nudge in the right direction!

Rails 4 fields_for form creates duplicate rows in database

I am new to rails and new to Stackoverflow so please bear with me. I am sure there are things beyond my current query that are not done correctly either.
I have an application which has matricies which have questions and submissions, and submissions have nested answers (to the questions in the matricies).
I have a form for users to submit submissions which displays all the relevant questions for the particular matrix and allows users to answer them. This all renders and saves correctly in the submissions/answers tables in the database.
However, when I edit a submission it correctly prepopulates the fields with the previously entered data, so that part is working correctly, but when I save it simply duplicates it all.
I am sure I have made some mistake in my relationships declarations as a submission should only be allowed one answer to each question, but I have been going round in circles for hours and could well be looking in the wrong place.
You can see the effect here: http://digital-maturity-matrix.herokuapp.com/matrices/1/submissions/2
My model:
class Submission < ActiveRecord::Base
belongs_to :matrix
belongs_to :user
has_many :answers, :dependent => :destroy
accepts_nested_attributes_for :answers,
:allow_destroy => true
end
My controller:
def edit
#matrix = Matrix.find(params[:matrix_id])
#submission = Submission.find(params[:id])
#answers = #submission.answers
#questions = #matrix.questions
end
def update
#matrix = Matrix.find(params[:matrix_id])
#user_id = current_user
respond_to do |format|
if #submission.update(submission_params)
format.html { redirect_to matrix_submission_path(#matrix,#submission), notice: 'Submission was successfully updated.' }
format.json { render :show, status: :ok, location: #submission }
else
format.html { render :edit }
format.json { render json: #submission.errors, status: :unprocessable_entity }
end
end
end
...
def submission_params
params.require(:submission).permit(:matrix_id, :user_id, :name, :answers_attributes => [:question_answered, :choice, :question_id])
end
My form:
= form_for([#matrix,#submission]) do |f|
- if #submission.errors.any?
#error_explanation
%h2
= pluralize(#submission.errors.count, "error")
prohibited this submission from being saved:
%ul
- #submission.errors.full_messages.each do |message|
%li= message
%br/
.field
= f.hidden_field :matrix_id, :value => params[:matrix_id]
.field
= f.hidden_field :user_id, :value => current_user.id
= f.fields_for :answers, #answers do |answer|
.field
=answer.label answer.object.question_answered
= answer.text_field :choice
.field
= answer.hidden_field :question_answered
.field
= answer.hidden_field :question_id
.actions
= f.submit
Within seconds of posting this I have found my answer. I think the process of writing the question helped.
For anyone else who stumbles across this, I had missed the inclusion of :id in my nested attributes permitted params. I hadn't realised this was required.
So I needed to replace:
def submission_params
params.require(:submission).permit(:matrix_id, :user_id, :name, :answers_attributes => [:question_answered, :choice, :question_id])
end
with:
def submission_params
params.require(:submission).permit(:matrix_id, :user_id, :name, :answers_attributes => [:id, :question_answered, :choice, :question_id])
end
Thanks all,
TB

Rails 4 form_for double nested comments

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.

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 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.

Resources