Can't mass-assign protected attributes: document - ruby-on-rails-3

I have two models Employee and Documents which are as follows:
Employee.rb
class Employee < ActiveRecord::Base
has_one :document #,dependent: :destroy
attr_accessible :age, :dob, :empNo, :first_name, :gender, :last_name, :middle_name, :document_attributes
accepts_nested_attributes_for :document
validates :first_name, presence: true , length: { maximum: 50 }
validates :empNo, presence: true, uniqueness:{ case_sensitive: false }
validates_length_of :empNo, :minimum => 5, :maximum => 5
#before save { |user| user.email = email.downcase }
end
Document.rb
class Document < ActiveRecord::Base
belongs_to :employee,foreign_key: "empno"
attr_accessible :idate, :iedate, :insuranceno, :iqamano, :iqedate, :iqidate, :passportno, :pedate, :pidate, :vedate, :vidate, :visano
end
and the controller file is employees_controller.rb(I have only shown new,create,show funcs)
def show
#employee = Employee.find(params[:id])
#document=Document.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #employee }
end
end
# GET /employees/new
# GET /employees/new.json
def new
#employee = Employee.new
#document= Document.new
respond_to do |format|
format.html # new.html.erb
format.json { render :json=>{:employee=> #employee,:document=>#document}, status: :created, :location=>{:employee=> #employee,:document=>#document} }
end
end
# POST /employees
# POST /employees.json
def create
#employee = Employee.create!(params[:employee])
#document = Document.create!(params[:document])
respond_to do |format|
if #employee.save and #document.save
format.html { redirect_to #employee, notice: 'Employee was successfully created.' }
format.json { render :json=>{:employee=> #employee,:document=>#document}, status: :created, location: #employee }
else
format.html { render action: "new" }
format.json { render json: #employee.errors, status: :unprocessable_entity }
end
end
end
When I create a new employee I get the following error
ActiveModel::MassAssignmentSecurity::Error in EmployeesController#create
Can't mass-assign protected attributes: document
The requsts parameters are fine as seen below
{"utf8"=>"✓",
"authenticity_token"=>"vXSnbdi+wlAhR5p8xXvTWhi85+AVZgOZufClx73gc8Q=",
"employee"=>{"empNo"=>"11111",
"first_name"=>"Thaha",
"middle_name"=>"Muhammed",
"last_name"=>"Hashim",
"age"=>"25",
"gender"=>"M",
"dob(1i)"=>"2014",
"dob(2i)"=>"7",
"dob(3i)"=>"18",
"document"=>{"passportno"=>"bycucde63"}},
"commit"=>"Create Employee"}
I have gone through nearly all posts on stackoverflow dealing with this issue and mostly the issue is related to
not using attr_accessible
not using accepts_nested_attributes_for
not using :document_attributes
If I change the value of config.active_record.whitelist_attributes to false then the error goes away(There is a warning in developer log about the same) and both the models are created but only attributes of employee model is filled with passed values wheras the attributes of document model is nil.
EDIT #1
If I tried to add :document to attr_accessible then I get the following error
ActiveRecord::AssociationTypeMismatch in EmployeesController#create
What is that I am doing wrong here?

Understanding Mass Assignment
Mass Assignment is the name Rails gives to the act of constructing your object with a parameters hash. It is "mass assignment" in that you are assigning multiple values to attributes via a single assignment operator.
The following snippets perform mass assignment of the name and topic attribute of the Post model:
Post.new(:name => "John", :topic => "Something")
Post.create(:name => "John", :topic => "Something")
Post.update_attributes(:name => "John", :topic => "Something")
In order for this to work, your model must allow mass assignments for each attribute in the hash you're passing in.
There are two situations in which this will fail:
You have an attr_accessible declaration which does not include :name
You have an attr_protected which does include :name
It recently became the default that attributes had to be manually white-listed via a attr_accessible in order for mass assignment to succeed. Prior to this, the default was for attributes to be assignable unless they were explicitly black-listed attr_protected or any other attribute was white-listed with attr_acessible.

The easiest way here would be, i suppose, to add :document to attr_accessible.
If an attribute is not listed there, ActiveRecord will not allow you to "mass-assign" them.
Relating to my comment, you'd end up with something like this:
...
"dob(3i)"=>"18",
"document"=><#Document ...>}, # {"passportno"=>"bycucde63"} is just a Hash, not a Document, that's why it raises "ActiveRecord::AssociationTypeMismatch"
"commit"=>"Create Employee"}
In the code, like:
def create
#document = Document.create!(params[:document])
#employee = Employee.create!(params[:employee].merge(:document => #document))

Related

How get the return value of checkbox in rails

I am sorry because this look like a doubled post, but i saw a lot of other threads and i cant understand anything that i am doing.
I am trying to make an has_and_belongs_to_many but i am stuck.
I managed to make the form display the right information, but i dont know how to save it.
I got:
Orb class:
class Orb < ActiveRecord::Base
attr_accessible :descr, :id, :nome, :orb_type_id, :orbt
validates_presence_of :nome, :orb_type_id
validates :nome, :uniqueness => true
belongs_to :orb_type
has_and_belongs_to_many :books
end
Book class:
class Book < ActiveRecord::Base
attr_accessible :dataf, :datai, :descr, :id, :nome
validates_presence_of :nome
validates :nome, :uniqueness => true
has_and_belongs_to_many :orbs
# allows project page to add items via checkboxes
accepts_nested_attributes_for :orbs
end
A _form:
<% #book.each do |book| %>
<div>
<%= check_box_tag "orb[book_ids][]", book.id, #orb.books.include?(book), id: dom_id(book) %>
<%= book.nome %>
</div>
<% end %>
And the controller:
def new
#book = Book.all
#orb = Orb.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #orb }
end
end
# GET /orbs/1/edit
def edit
#orb = Orb.find(params[:id])
#book = Book.all
end
# POST /orbs
# POST /orbs.json
def create
#orb = Orb.new(params[:orb])
respond_to do |format|
if #orb.save
format.html { redirect_to #orb, notice: 'save was successful' }
format.json { render json: #orb, status: :created, location: #orb }
else
format.html { render action: "Novo" }
format.json { render json: #orb.errors, status: :unprocessable_entity }
end
end
end
# PUT /orbs/1
# PUT /orbs/1.json
def update
params[:orb][:book_ids] ||= []
#orb = Orb.find(params[:id])
respond_to do |format|
if #orb.update_attributes(params[:orb])
format.html { redirect_to #orb, notice: 'save was successful' }
format.json { head :no_content }
else
format.html { render action: "Editar" }
format.json { render json: #orb.errors, status: :unprocessable_entity }
end
end
end
With this, the form has a checkbox with the right values, but it wont be save anywere.
I dont know what i am doing, can some one me explain what i have to do?
You need a join table to use has_and_belongs_to_many
class CreateTableBooksOrbs < ActiveRecord::Migration
def change
create_table :books_orbs, :id => false do |t|
t.references :orb, :null => false
t.references :book, :null => false
end
add_index :books_orbs, [:book_id, :orb_id], :unique => true
end
end
see here my working version : https://github.com/senayar/books

Rails 3 basic validation not working with :prompt or :include_blank?

I have the following code in my view:
<%= f.select :user_id, user_all_select_options, :include_blank => '--Select a name-----' %>
It displays a list of users with --Select a name----- at the top. When a user doesn't select a name from the list and leaves --Select a name----- selected I get errors because the user_id is blank.
For reference the helper method code looks like this:
def user_all_select_options
User.all.map{ |user| [user.name, user.id] }
end
My model is as follows:
class Parcel < ActiveRecord::Base
attr_accessible :parcel, :received_at, :received_by, :chilled, :courier, :location, :passed_to, :user_id, :user, :content, :localip
validates :user_id, :presence => true
belongs_to :user
end
For some reason the validation doesn't appear to be running, however if I select a user from the drop down and add a validation for an other field input the application correctly shows the user a message stating which field is incorrectly empty.
Interestingly if I leave the select drop down as --Select a name----- and keep the additional validation, an exception is thrown. It doesn't prompt for the missing fields it just errors.
Here is the record during an error (this record was from when I had a validates presence check on the location field:
{"utf8"=>"✓", "authenticity_token"=>"wM4KPtoswp3xdv8uU4UasdadNsZi9yFZmk=", "parcel"=>{"user_id"=>"", "received_by"=>"dan", "content"=>"", "chilled"=>"0", "courier"=>"", "location"=>"", "passed_to"=>"", "received_at(3i)"=>"9", "received_at(2i)"=>"2", "received_at(1i)"=>"2013", "received_at(4i)"=>"22", "received_at(5i)"=>"59"}, "commit"=>"Receive this Parcel", "action"=>"create", "controller"=>"parcels"}
Where should I start looking? The errors that show are when the controller does an unless check against the user.
The parcel controller create method looks like this:
def create
#parcel = Parcel.new(params[:parcel])
#parcel.localip = request.env['REMOTE_ADDR']
#parcel.received_by = #parcel.received_by.upcase
unless #parcel.user.mobilenumber.blank?
UserMailer.parcel_notification(#parcel).deliver
end
respond_to do |format|
if #parcel.save
format.html { redirect_to #parcel, notice: 'Parcel was successfully created.' }
format.json { render json: #parcel, status: :created, location: #parcel }
else
format.html { render action: "new" }
format.json { render json: #parcel.errors, status: :unprocessable_entity }
end
end
end
the reason why you're getting an exception when you don't select a user is this line
unless #parcel.user.mobilenumber.blank?
since the user_id is not set, #parcel.user is nil which causes the exception.
I suggest you move that inside the #parcel.save block.
def create
#parcel = Parcel.new(params[:parcel])
#parcel.localip = request.env['REMOTE_ADDR']
#parcel.received_by = #parcel.received_by.upcase
respond_to do |format|
if #parcel.save
unless #parcel.user.mobilenumber.blank?
UserMailer.parcel_notification(#parcel).deliver
end
format.html { redirect_to #parcel, notice: 'Parcel was successfully created.' }
format.json { render json: #parcel, status: :created, location: #parcel }
else
format.html { render action: "new" }
format.json { render json: #parcel.errors, status: :unprocessable_entity }
end
end
end

Rails validation count limit on has_many :through

I've got the following models: Team, Member, Assignment, Role
The Team model has_many Members. Each Member has_many roles through assignments. Role assignments are Captain and Runner. I have also installed devise and CanCan using the Member model.
What I need to do is limit each Team to have a max of 1 captain and 5 runners.
I found this example, and it seemed to work after some customization, but on update ('teams/1/members/4/edit'). It doesn't work on create ('teams/1/members/new'). But my other validation (validates :role_ids, :presence => true
) does work on both update and create. Any help would be appreciated.
Update: I've found this example that would seem to be similar to my problem but I can't seem to make it work for my app.
It seems that the root of the problem lies with how the count (or size) is performed before and during validation.
For Example:
When updating a record...
It checks to see how many runners there are on a team and returns a count. (i.e. 5) Then when I select a role(s) to add to the member it takes the known count from the database (i.e. 5) and adds the proposed changes (i.e. 1), and then runs the validation check. (Team.find(self.team_id).members.runner.count > 5) This works fine because it returns a value of 6 and 6 > 5 so the proposed update fails without saving and an error is given.
But when I try to create a new member on the team...
It checks to see how many runners there are on a team and returns a count. (i.e. 5) Then when I select a role(s) to add to the member it takes the known count from the database (i.e. 5) and then runs the validation check WITHOUT factoring in the proposed changes. This doesn't work because it returns a value of 5 known runner and 5 = 5 so the proposed update passes and the new member and role is saved to the database with no error.
Member Model:
class Member < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :password, :password_confirmation, :remember_me
attr_accessible :age, :email, :first_name, :last_name, :sex, :shirt_size, :team_id, :assignments_attributes, :role_ids
belongs_to :team
has_many :assignments, :dependent => :destroy
has_many :roles, through: :assignments
accepts_nested_attributes_for :assignments
scope :runner, joins(:roles).where('roles.title = ?', "Runner")
scope :captain, joins(:roles).where('roles.title = ?', "Captain")
validate :validate_runner_count
validate :validate_captain_count
validates :role_ids, :presence => true
def validate_runner_count
if Team.find(self.team_id).members.runner.count > 5
errors.add(:role_id, 'Error - Max runner limit reached')
end
end
def validate_captain_count
if Team.find(self.team_id).members.captain.count > 1
errors.add(:role_id, 'Error - Max captain limit reached')
end
end
def has_role?(role_sym)
roles.any? { |r| r.title.underscore.to_sym == role_sym }
end
end
Member Controller:
class MembersController < ApplicationController
load_and_authorize_resource :team
load_and_authorize_resource :member, :through => :team
before_filter :get_team
before_filter :initialize_check_boxes, :only => [:create, :update]
def get_team
#team = Team.find(params[:team_id])
end
def index
respond_to do |format|
format.html # index.html.erb
format.json { render json: #members }
end
end
def show
respond_to do |format|
format.html # show.html.erb
format.json { render json: #member }
end
end
def new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #member }
end
end
def edit
end
def create
respond_to do |format|
if #member.save
format.html { redirect_to [#team, #member], notice: 'Member was successfully created.' }
format.json { render json: [#team, #member], status: :created, location: [#team, #member] }
else
format.html { render action: "new" }
format.json { render json: #member.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #member.update_attributes(params[:member])
format.html { redirect_to [#team, #member], notice: 'Member was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #member.errors, status: :unprocessable_entity }
end
end
end
def destroy
#member.destroy
respond_to do |format|
format.html { redirect_to team_members_url }
format.json { head :no_content }
end
end
# Allow empty checkboxes
# http://railscasts.com/episodes/17-habtm-checkboxes
def initialize_check_boxes
params[:member][:role_ids] ||= []
end
end
_Form Partial
<%= form_for [#team, #member], :html => { :class => 'form-horizontal' } do |f| %>
#...
# testing the count...
<ul>
<li>Captain - <%= Team.find(#member.team_id).members.captain.size %></li>
<li>Runner - <%= Team.find(#member.team_id).members.runner.size %></li>
<li>Driver - <%= Team.find(#member.team_id).members.driver.size %></li>
</ul>
<div class="control-group">
<div class="controls">
<%= f.fields_for :roles do %>
<%= hidden_field_tag "member[role_ids][]", nil %>
<% Role.all.each do |role| %>
<%= check_box_tag "member[role_ids][]", role.id, #member.role_ids.include?(role.id), id: dom_id(role) %>
<%= label_tag dom_id(role), role.title %>
<% end %>
<% end %>
</div>
</div>
#...
<% end %>
Try
class Member < ActiveRecord::Base
...
def validate_runner_count
if self.team.members.runner.count > 5
errors.add(:role_id, 'Error - Max runner limit reached')
end
end
def validate_captain_count
if self.team.members.captain.count > 1
errors.add(:role_id, 'Error - Max captain limit reached')
end
end
end

Rails 3 - Building a nested resource within another nested resource (articles -> comments -> votes)

In my app there is an association problem, which I'm unable to fix.
My app is quite simple: There's an Article model; each article has_many comments, and each of those comments has_many votes, in my case 'upvotes'.
To explain the way I designed it, I did a comments scaffold, edited the comment models and routes to a nested resource, everything works fine. Now, I basically did the same process again for 'upvotes' and again edited model and routes to make this a nested resource within the comment nested resource. But this fails at the following point:
NoMethodError in Articles#show
Showing .../app/views/upvotes/_form.html.erb where line #1 raised:
undefined method `upvotes' for nil:NilClass
My _form.html.erb file looks like this:
<%= form_for([#comment, #comment.upvotes.build]) do |f| %>
<%= f.hidden_field "comment_id", :value => :comment_id %>
<%= image_submit_tag "buttons/upvote.png" %>
<% end %>
Why is 'upvotes' undefined in this case, whereas here:
<%= form_for([#article, #article.comments.build]) do |form| %>
rest of code
everything works totally fine? I copied the same mechanism but with #comment.upvotes it doesn't work.
My upvotes_controller:
class UpvotesController < ApplicationController
def new
#upvote = Upvote.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #upvote }
end
end
def create
#article = Article.find(params[:id])
#comment = #article.comments.find(params[:id])
#upvote = #comment.upvotes.build(params[:upvote])
respond_to do |format|
if #upvote.save
format.html { redirect_to(#article, :notice => 'Voted successfully.') }
format.xml { render :xml => #article, :status => :created, :location => #article }
else
format.html { redirect_to(#article, :notice =>
'Vote failed.')}
format.xml { render :xml => #upvote.errors, :status => :unprocessable_entity }
end
end
end
end
I'm sorry for this much code.., my articles_controller: (extract)
def show
#upvote = Upvote.new(params[:vote])
#article = Article.find(params[:id])
#comments = #article.comments.paginate(page: params[:page])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #article }
end
end
And my 3 models:
class Article < ActiveRecord::Base
attr_accessible :body, :title
has_many :comments
end
class Comment < ActiveRecord::Base
attr_accessible :content
belongs_to :user
belongs_to :article
has_many :upvotes
end
class Upvote < ActiveRecord::Base
attr_accessible :article_id, :comment_id, :user_id
belongs_to :comment, counter_cache: true
end
Upvote migration file:
class CreateUpvotes < ActiveRecord::Migration
def change
create_table :upvotes do |t|
t.integer :comment_id
t.integer :user_id
t.timestamps
end
end
end
My routes:
resources :articles do
resources :comments, only: [:create, :destroy] do
resources :upvotes, only: [:new, :create]
end
end
Sorry for that much code. If anyone might answer this, they would be so incredibly awesome!
Thank you in advance!
Why is 'upvotes' undefined in this case, whereas here:
This is because you're calling upvotes on a nil object, the comment doesn't exist yet.
Best thing to do would be looking into nested attributes:
http://guides.rubyonrails.org/2_3_release_notes.html#nested-attributes
http://guides.rubyonrails.org/2_3_release_notes.html#nested-object-forms
Your error message, says that you try call upvotes on nil. Specifically it is a part of code #comment.upvotes.build in your /app/views/upvotes/_form.html.erb view.
You have to fix show action in you ArticlesController, by adding #comment (with contents) variable.
def show
#upvote = Upvote.new(params[:vote])
#article = Article.find(params[:id])
#comments = #article.comments.paginate(page: params[:page])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #article }
end
end
Also strange things are happening in UpvotesController, in create action.
#article = Article.find(params[:id])
#comment = #article.comments.find(params[:id])
#upvote = #comment.upvotes.build(params[:upvote])
Firstly you had fetched one #article using params[:id], then you had fetched all comments of that #article (throught association), where comments id is the same as #article id. Please review your code, it is inconsistent and will not work correctly.
Everything fixed and works fine now. Took a different approach and simply used Upvote.new instead of nesting it into the comments and building associations, edited my routes as well. Implemented Matthew Ford's idea
I would suspect you have many comments on the article page, the comment variable should be local e.g #article.comments.each do |comment| and then use the comment variable to build your upvote forms.
Thanks everybody for your help!

Rails 3 -- Build not saving to database (Railscast 196)

I'm following along with railscast 196. I've got two levels of associations. App -> Form -> Question. This is the new action in the form controller.
def new
#app = App.find(params[:app_id])
#form = Form.new
3.times {#form.questions.build }
end
the view is displaying all 3 questions fine and I can submit the form... but nothing is inserted into the database for the questions. Here is my create action
def create
#app = App.find(params[:app_id])
#form = #app.forms.create(params[:form])
respond_to do |format|
if #form.save
format.html { redirect_to(:show => session[:current_app], :notice => 'Form was successfully created.') }
format.xml { render :xml => #form, :status => :created, :location => #form }
else
format.html { render :action => "new" }
format.xml { render :xml => #form.errors, :status => :unprocessable_entity }
end
end
end
Here are the params that are sent to my create method:
{"commit"=>"Create Form",
"authenticity_token"=>"Zue27vqDL8KuNutzdEKfza3pBz6VyyKqvso19dgi3Iw=",
"utf8"=>"✓",
"app_id"=>"3",
"form"=>{"questions_attributes"=>{"0"=>{"content"=>"question 1 text"},
"1"=>{"content"=>"question 2 text"},
"2"=>{"content"=>"question 3 text"}},
"title"=>"title of form"}}`
This shows that the params are being sent correctly... I think. The question model just has a "content" text column.
Any help appreciated :)
Assuming:
You have your form set up properly,
Your server shows your data is being sent to the new action, and
Your model doesn't contain callbacks that are blocking the save,
try changing:
#form = #app.forms.create(params[:form])
to
#form = #app.forms.build(params[:form])
Ok figured it out. Turns out I should have been looking at my console a little more. The error it was hanging up on when trying to insert questions into the db was "Warning: can't mass assign protected attributes :questions_attributes". Adding this into the accessible attributes did the trick.
class Form < ActiveRecord::Base
belongs_to :app
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions
attr_accessible :title, :questions_attributes
end