Rails: Reference to new Model throwing error - ruby-on-rails

I have a working users table.
I have generated a new table using:
rails generate model quiz_answers
..and run rake db:migrate
My CreateQuizAnswers migration file looks like this:
class CreateQuizAnswers < ActiveRecord::Migration
def change
create_table :quiz_answers do |t|
t.references :user, index: true
t.string :answer1
t.string :answer2
t.string :answer3
t.string :answer4
t.string :answer5
t.string :answer6
t.string :answer7
t.string :answer8
t.timestamps
end
end
end
I have a quiz_answer model:
class QuizAnswer < ActiveRecord::Base
belongs_to :user
end
and a QuizAnswersController:
class QuizAnswersController < ApplicationController
def new
#user = current_user
#quiz_answer = current_user.quiz_answer.build
end
private
def post_params
params.require(:quiz_answer).permit(:body, :user_id)
end
end
I have added :quiz_answers as a resource in routes.rb
Edited question:
WHY, then, when I try to build a form (using Devise) do I get the error "undefined method `body' for...(with a reference to QuizAnswer here)"? I have another model 'Post' which does not generate this error and does not have a 'body' attribute.
The page where I'm trying to build the form is home/whattypeofleader.html.erb and, in routes.rb I have:
get "whattypeofleader" => "home#whattypeofleader"
And in my HomeController I have:
class HomeController < ApplicationController
def index
end
def whattypeofleader
#user = current_user
#quiz_answer = current_user.quiz_answer.build
end
end
What the HELL am I doing wrong? Any help desperately appreciated, thanks.
Oh, and in case you need it, here's the form code, a partial that gets 'rendered' in whattypeofleader:
<%= form_for([current_user, #quiz_answer]) do |f| %>
<p>
<%= f.text_area :body, :autofocus => true , :class => "elearning-input"%>
</p>
<p>
<%= f.submit("Save entry") %>
</p>
<% end %>

<%= form_for([current_user, #quiz_answer]) do |f| %>
#...
<%= f.text_area :body, :autofocus => true , :class => "elearning-input"%>
You are creating a text_area here, and you tell rails to bind this text area input with a #quiz_answer.body attribute. Why?
form_for assigns last element of an array to be a form's object. It yield a form builder object (f), which keeps a reference to this object (f.object). All the fields created with this form builder are automatically populated with a value of f.object.send(:field_name) (it also set up a name of the fields, so the params can be easily matched when it is posted). This is rails magic making all the form fields populated with model's data.
Since your model does not have body attribute (no such column in your database and no method with that name defined), f.object.send(:body) throws a method undefined error.
Regarding the fix to that, you need to decide how this form is to look for. Your models contain 8 columns, answer<i> (which is worrying and suggests you might wanna use association here).

Related

Permitting params with custom keys

I have a model names TeamInvite that I am trying to create a team_invite_params method for.
When I created the migration for the table, because I wanted to keep track of the sender and recipient, I opted for the custom field names
sender_id
and
recipient_id
to not mix up which user was which.
Anyway, when I try and permit the parameters for a team_invite, I am having trouble telling the permit method that the
Edit:
"team_invite"=>{"user"=>"2"}
is in fact what should go under recipient_id. All the methods I have tried either don't work or end up passing no value at all.
Controller
def create
#team_invite = TeamInvite.new(team_invite_params)
end
Model
class TeamInvite < ApplicationRecord
belongs_to :recipient, class_name: "User"
belongs_to :sender, class_name: "User"
end
Params:
{"utf8"=>"✓", "authenticity_token"=>"0QLA9YiTu9nAf6QJLX5rg0DWa7CAMjqGOlUBICbcL8Ucs2DsCDgGBnhiDXPa/ot8DK0xwTR1D7MASu4/9tVj0w==", "team_invite"=>{"recipient_id"=>"2"}, "commit"=>"Save Team invite"}
View (if it matters):
<%= form_for :team_invite, url: team_invites_path do |f| %>
<%= f.select :recipient_id, User.all.collect { |p| [ p.name, p.id ] }, include_blank: false %>
<%= f.submit %>
<% end %>
Migration:
class CreateTeamInvite < ActiveRecord::Migration[5.0]
def change
create_table :team_invites do |t|
t.references :team, foreign_key: true
t.integer :recipient_id
t.integer :sender_id
t.timestamps
end
end
end
You need:
params.permit(:team_invites => [ :user ]) #or whatever other nested team_invite params you want to pass in.
Docs for strong_params here: https://github.com/rails/strong_parameters

Rails: ActiveRecord Association returns a nil relationship

I'm trying to print all the comments and their associated users for a post on a blog. The comments are passed into the view, and their users_id and posts_id are accessible, but I cannot access their users; comment.user returns nil.
Model:
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
Migration:
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :text
t.references :users
t.references :posts
t.timestamps
end
end
end
In the controller:
#post = Post.find(params[:id])
#comments = Comment.where(posts_id: params[:id]).order(:created_at)
In the view:
<% #comments.each do |c| %>
<% c.user.nil? %>
<p><%=c.text%></p>
<p><%=c.users_id%></p>
<p><%=c.posts_id%></p>
<% else %>
<p><%=c.text%></p>
<p><%=c.user.name%></p> #Here is where things would break if I didn't check "c.user.nil?"
<% end %>
<% end %>
The text and ids for the comments print as expected, but the user is still considered nil. Does anyone know how to access user from within the comment model in the view?
I believe the issue are the names of the foreign key columns in your database. When declaring a post association while explicitly stating the foreign key (you could do this), it's going to look for post_id on the model. So, you should either change your column in the DB to post_id, or change the association method call to this:
belongs_to :post, foreign_key: :posts_id
In your controller:
Change:
#comments = Comment.where(posts_id: params[:id]).order(:created_at)
To:
#comments = Comment.where(post_id: params[:id]).order(:created_at)
You should have a post_id column in your comments table in the database. Then, you can have access to the user for a given comment like this:
comment.user
and eventually:
comment.user.name
will work too!
Your migrations does not seems right to complete what #Jake Shorty said, I would say When doing :
t.references :users
Rails will actually transform it such as :
t.integer 'users_id'
which does not match with the default Rails conventions, so it cannot retrieve the associated user using user_id foreign key.
To fix it, you need to either change the migration, or change your model :
class Comment < ActiveRecord::Base
belongs_to :user, foreign_key: :users_id
end

How do I fix NoMethodError when trying to create a pin in ruby?

Excuse me for maybe being naive, this is my first time using StackOverflow and I'm trying to learn ruby. I'm making an application through onemonthrails.com's tutorial that is similar to pinterest. I'm trying to add a pin and I keep getting the error:
NoMethodError in Pins#new
Showing /Users/jake/code/omrails/app/views/pins/_form.html.erb where line #5 raised:
undefined method `description' for #
Extracted source (around line #5):
I dont know what all will help you answer the question so I'll post all the files I received (that may be relevant to the question) when I ran the following command:
$ rails generate scaffold Pins
the migration file:
class CreatePins < ActiveRecord::Migration
def change
create_table :pins do |t|
t.string :description
t.timestamps
end
end
end
the model:
class Pin < ActiveRecord::Base
attr_accessible :description
end
_form.html.erb: (This is where it found the error)
<%= simple_form_for(#pin) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :description %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
new.html.erb: (this is "trace of template conclusion")
<h1>New pin</h1>
<%= render 'form' %>
<%= link_to 'Back', pins_path %>
I don't understand the error because I thought the method was defined in the model under attr_accessible :description
I'd appreciate your help if you can understand anything i just said. If not thanks for taking the time to look at it.
You need to run the migrations to create the pins table.
rake db:migrate
If it complains that the table exists, this means that you ran the migration before adding the description column. Rerunning the migration won't work without first reverting it:
rake db:migrate:redo
To clarify your point about attr_accessible :description - attr_accessible does not define the attribute for your model. Your database table does that (usually as per your migrations). What attr_accessible does is act as a whitelist for mass-assignable attributes.
I'm assuming you did not rake db:migrate after you created your scaffold. Try that first.
I don't understand the error because I thought the method was defined
in the model under attr_accessible :description
The attr_accessible :description declaration doesn't actually define the method. ActiveRecord does that under the hood for you based on the columns in your pins table. If you did not run the migration (and therefore create your table) ActiveRecord will not be able to auto generate attribute methods on your model, which could very well cause the error you're seeing.
attr_accessible declares your attributes accessible for being set via mass assignment. Typically, when you do this in your controller: #pin = Pin.new params[:pin] where params[:pin] is a hash of your model's attributes.
Could you check you database table "pins" having description column? If not, try to drop your previous table and try to create new one with description column.
or you can create a migration file to update your existing table, like:
rails g migration add_description_to_pins
then inside this file:
class AddDescriptionToPins < ActiveRecord::Migration
def self.up
add_column :pins, :description, :string
end
def self.down
remove_column :pins, :description
end
end

Select many box for HABTM

I have two models; question and category which have a HABTM association between them. Now I want to have a form where I can edit the questions categories, however I don't know how. I started with this but I am lost, I am unsure on what to name the "name" attributes etc and how it is automatically edited/created with the question, how do I set this up?
<%= f.fields_for :categories do |categories_form| %>
<%= categories_form.select "category_ids", Category.all.collect { |c| [c.description, c.id] }, {}, {:multiple => true, :size => 9} %>
<% end %>
I managed to set up question(has_many) --> answer with fields_for and accepts_nested_attributes_for, but not this.
You should take a look at the following screencasts by Ryan Bates Nested Model Form Part 1 and Nested Model Form Part 2.
Migrations
You need to create the migrations for the tables
You need to create the migration for the middle table of the association
+ the middle table name that is created by the association is :categories_questions
or :questions_categories, in the second case you must define the name in models as shown in the link
Do I need to manually create a migration for a HABTM join table?
class CreateCategoriesQuestions < ActiveRecord::Migration
def self.up
create_table :categories_questions, :id => false do |t|
t.references :category
t.references :question
end
add_index :categories_questions, [:category_id, :question_id]
add_index :categories_questions, [:question_id, :category_id]
end
def self.down
drop_table :categories_questions
end
end
Question Model
class Question < ActiveRecord::Base
has_and_belongs_to_many :categories
end
Category Model
class Category < ActiveRecord::Base
has_and_belongs_to_many :questions
end
Controller Stuf
questions_controller.rb
def new
#question = Question.new
#question.categories.build #Build a categories_questions so as to use fields_for
end
Form Stuff
= f.fields_for :categories do |categories_fields|
= categories_fields.text_field :name
= categories_fields.text_field :description
At this point i must tell you ( i am new in ruby & rails ) that to create a new object here you can use jquery to append a html block name properly, or create helpers (that use javascript in the end) to add a new object and on save, save the association.
In the next link someone demonstrated the exact way .
http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for#512-Setting-child-index-while-using-nested-attributes-mass-assignment

HABTM Form not working in rails. Not submiting into Join tables

I am trying to assign a team to a player. A player can have many teams but upon initial set up they only have one. I am trying to set up a choice so the user can pick a team.
When I submit the form the data for the other fields, first_name and last_name submit fine however no data gets saved in the join table. I am using a select box however check boxes will do the job too if someone can think of that.
Models
I have 2 models. Players and Teams set up with a HABTM relationship.
class Player < ActiveRecord::Base
has_and_belongs_to_many :teams
class Team< ActiveRecord::Base
has_and_belongs_to_many :players
Controller
def new
#player = Player.new
end
def create
#player = Player.new(params[:player])
if #player.save
flash[:notice] = "Player Created"
redirect_to(:action =>'list')
else
render('new')
end
end
Join table
I can insert data into the the join table from the rails console. I can then get this data and show it. So the relationships work.
create_table "players_teams", :id => false, :force => true do |t|
t.integer "player_id"
t.integer "team_id"
end
add_index "players_teams", ["player_id", "team_id"], :name => "index_players_teams_on_player_id_and_team_id"
View
In my view I have this
<%= f.collection_select(:team_id, Team.all, :id, :name, :prompt => true) %>
This loads a view with the teams Populated. Once submitted I get
Parameters: {"utf8"=>"✓", "authenticity_token"=>"kvOmx3G5H1mqLMnEn6HS3a79+WQnIzfsUA3Dt0XHo1w=", "player"=>{"first_name"=>"Test", "last_name"=>"Data", "email"=>"email#email.com"}, "teams"=>{"team_id"=>"1"}, "commit"=>"create player"}
I am not sure where to go from here.
Where on the form are you putting the collection? Maybe it is a typo but is should be
<%= form_for(#player) do |f| %>
...
<%= f.collection_select(:team_ids, Team.all, :id, :name, :prompt => true) %>
...
<% end %>
and you should get
Parameters: {
"utf8"=>"✓", "authenticity_token"=>"kvOmx3G5H1mqLMnEn6HS3a79+WQnIzfsUA3Dt0XHo1w=",
"player"=>{
"first_name"=>"Test",
"last_name"=>"Data",
"email"=>"email#email.com",
"team_ids"=>["1"]
},
"commit"=>"create player"
}
which means that the team_ids attribute gets set by the form.
Your parameter is incorrect. Since a player has_and_belongs_to_many :teams, the proper parameter is team_ids. However, that should be an array, so you need something like this:
<%= f.collection_select("team_ids[]", Team.all, :id, :name, :prompt => true) %>
I believe that should do it.

Resources