So I am following one tutorial that is obviously done in rails 3 and I am using rails 4. I get this error:
ActiveModel::ForbiddenAttributesError
With this code:
def create
#movie = Movie.create!(params[:movie])
flash[:notice] = "#{#movie.title} was successfully created."
redirect_to movies_path
end
Obviously it has something with strong param
You need to make sure that all attributes required to create a Movie are whitelisted.
Define a method like this in your controller:
private
def movie_params
params.require(:movie).permit(:title, :rating, :release_date)
end
And then pass the result of the method into create!:
def create
#movie = Movie.create!(movie_params)
# ...
end
Read more about strong parameters in the Rails documentation.
Related
As the title says. I know that strong parameters is to prevent other unauthorized attributes to be included when updating or creating new objects. I've seen codes that doesn't have strong parameters. For example in Hartl's tutorial relationships controller:
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create
#user = User.find(params[:followed_id])
current_user.follow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
end
and others have it included such as creating new post or user etc. so my question is, when is the practice to use strong parameters?
Its ideal to use strong parameters when mass assigning values. Like creating new post or user. It may allows attackers to set any database column’s value.
Check out Rails official guide for it.
Doing like these is fine, as long as you know and mentioning model attributes.
#user = User.find(params[:followed_id])
#user = Relationship.find(params[:id]).followed
I would say - use strong params for any actions where you use mass-assignment. This means that, actions like create or update must employ strong params.
For example, instead of having:
#object.update_attributes(params[:object])
Just have a:
#object.update_attributes(object_params)
Which will whitelist params for you. Also, it allows you to pass-through different params for different actions, with methods like object_update_params and object_create_params which will whitelist params for update and params for create, respectively.
Yes, not using strong parameters will raise ActiveModel::ForbiddenAttributesError so it's not optional unless you manage to override this behavior.
In the above example he is just retrieving a record and then creating a relationship with that id in the model.
def follow!(other_user)
relationships.create!(followed_id: other_user.id)
end
I'm having what I assume must be a simple problem but I just can't figure it out. I'm trying to update an attribute in one model when another is created.
In my view:
<%= link_to 'Click here to rate this user', new_user_review_path(:user_id => request.user.id, :gigid => request.gig.id), remote: true %>
Which passes params :gigid and :user_id
Than my controller:
def new
#review = Review.new
#gig = Gig.find(params[:gigid])
end
def create
#review = #user.reviews.new review_params
#review.reviewed_id = current_user.id
if #review.save
#gig.update(reviewed: true)
respond_to do |format|
format.html {redirect_to session.delete(:return_to), flash[:notice] = "Thankyou for your rating!"}
format.js
end
else
render 'new'
end
end
But I get undefined method 'update'for nil:NilCLass:
I know the params are passing and the 'Gig' can be updated as :
def new
#review = Review.new
Gig.find(params[:gigid]).update(reviewed: true)
end
updates the attribute fine, but when I click 'New review' not when the review is actually created.
Adding :
def create
#review = #user.reviews.new review_params
#review.reviewed_id = current_user.id
if #review.save
Gig.find(params[:gigid]).update(reviewed: true)
etc etc etc
gives me the same undefined method 'update'for nil:NilCLass:
I have tried with find_by_id instead of find which makes no difference.
EDIT:
def create
#gig = Gig.find params[:gigid]
#review = #user.reviews.new review_params
#review.reviewed_id = current_user.id
if #review.save
#gig.update(reviewed: true)
etc etc etc
Doesn't work either. I get no errors, but the gig ID is still 'nil'.
The params are passing to the 'New' action but not the 'Create' action. I feel this should be very easy but I'm just not seeing it at the moment.
But I get undefined method 'update'for nil:NilCLass:
The error is that you have not defined #gig in your create action.
Since Rails is built on HTTP, and HTTP is stateless, you have to set the "instance" variables with each new request:
def new
#review = Review.new
#gig = Gig.find params[:gigid]
end
def create
#gig = Gig.find params[:gigid]
#review = #user.reviews.new review_params
A much better pattern for you would be to use the after_create callback in your Review model:
#app/models/review.rb
class Review < ActiveRecord::Base
belongs_to :gig #-> I presume
after_create :set_gig
private
def set_gig
self.gig.update(reviewed: true)
end
end
--
If you wanted to make the Gig update within your current setup, you'll be best sending the gig_id param through the request (not the link):
#app/views/reviews/new.html.erb
<%= form_for [#user, #review] do |f| %>
<%= f.hidden_field :gig_id, #gig.id %> #-> params[:reviews][:gig_id]
...
<% end %>
This will make params[:review][:gig_id] available in the create action, with which you'll be able to use in your code.
The problem is, you never assigned a value to #gig in your create method. I can't see your form, but you need something like this in your create method:
#gig = Gig.find params[:gigid]
Assuming that you're passing the parameter :gigid to #create
In the second example you showed, I'm not sure what's going on, but you should be getting a ActiveRecord::RecordNotFound exception on the find().
Try the below code for update operation.
gig_record = Gig.find_by_id(params[:gigid])
gig_record.update_attribute(reviewed: true) unless gig_record.blank?
So I got a very basic blog app running with three links to a blog post. However, when I click on a post of my selection and edit the post and click on "update blog", I will get an error saying NameError in BlogsController#update and undefined local variable or method 'blog_params' for blogscontroller. I cannot figure out what the issue is so I would like some help to guide me through
This is what my blogs controller file looks like
class BlogsController < ApplicationController
def index
#blogs = Blog.all
end
def show
#blog = Blog.find(params[:id])
end
def new
#blog = Blog.new
end
def create
#blog = Blog.new
#blog.title = params[:blog][:title]
#blog.body = params[:blog][:body]
#blog.save
redirect_to blog_path(#blog)
end
def destroy
#blog = Blog.find(params[:id])
#blog.destroy
redirect_to blog_path(#blog)
end
def edit
#blog = Blog.find(params[:id])
end
def update
#blog = Blog.find(params[:id])
#blog.update(blog_params)
redirect_to blog_path(#blog)
end
end
def update
#blog = Blog.find(params[:id])
**#blog.update(blog_params)**
redirect_to blog_path(#blog)
end
here you are calling blog_params but you haven't defined it anywhere in your code.
See example here:
See strong params
You need to do this:
#app/controllers/blogs_controller.rb
class BlogsController < ApplicationController
def update
#blog = Blog.find params[:id]
#blog = #blog.update blog_params
end
private
def blog_params
params.require(:blog).permit(:title, :body) #-> in "permit" put the attributes of your "blog" model
end
end
The error is a standard programming issue - undeclared function.
Because you're starting - and to give you more context - the reason this is a problem is because you're calling blog_params when you run the .update method:
#blog.update blog_params
This, as mentioned by Pardeep Dhingra, is the strong params pattern introduced into Rails 4. Strong params prevents mass assignment by explicitly permitting particular attributes of your model.
Whilst your code is okay, you lack the strong params method (in your case blog_params), which Rails needs to complete the request. Adding the method will fix the issue.
My QuizzesController#index action looks like this:
def index
#user = current_user
#quiz = Quiz.create(user_id: current_user.id)
end
My view draws the quiz form fine. It goes to the results/index view as intended. BUT the various attributes of the quiz are NOT updated on the Quiz instance which is pulled from the database, in the QuizzesContoller#update action:
def update
#results = Quiz.where(user_id: current_user.id).last
redirect_to results_path
end
('update' is called in this case because the Quiz instance already exists, having been created in the 'index' action).
So, I tried changing the 'update' action to:
def update
#quiz.save
#results = Quiz.where(user_id: current_user.id).last
redirect_to results_path
end
But this triggers the error:
undefined method 'save' for nil:NilClass
Why is that? Shouldn't my QuizzesController have access to the #quiz variable as set up in the 'index' action? Can anyone explain what the problem is there?
Others have answered this question, so I thought I would explain why the answer is what it is. In Ruby, variables that begin with the # symbol are instance variables. This means that they are created when a new instance of their parent object is instantiated and are unique to that instance of the object.
Rails based web apps, for the most part, are stateless, meaning that state is not persisted between http requests. In layman terms, the app treats each and every request independent of all other requests. Due to this, the controllers are instanced classes. Every request instantiates a new instance of the controller class.
EDIT:
More I look at your code, you aren't following proper conventions
class QuizzesController < ApplicationController
# GET index: for displaying a list of quizzes
def index
#quizzes = Quiz.where(user_id: current_user.id)
end
# GET show: for getting a single quiz record
def show
#quiz = Quiz.find(params[:id])
end
# GET new: for initializing a new quiz record
def new
#quiz = Quiz.new
end
# POST create: for saving a new quiz record
def create
#quiz = current_user.quizzes.create(quiz_params)
if #quiz.errors
render :new
else
redirect_to #quiz #or whereever
end
end
# GET edit: for initializing existing quiz for update
def edit
#quiz = Quiz.find(params[:id)
end
# PUT/PATCH update: for updating an existing quiz record
def update
#quiz = Quiz.find(params[:id])
if #quiz.update(quiz_params)
redirect_to #quiz # or whereever
else
render :edit
end
# DELETE destroy: for deleting a quiz record
def destroy
Quiz.find(params[:id]).destroy
redirect_to :index # or whereever
end
end
You have not #quiz variable in your update action. Actions in the controller does not have access to variables in other actions.
The QuizzesController instance is not persisted between requests. The real reason instance variables are used in controllers is to pass that variable to the view.
A normal update action would look something like:
def update
#quiz = current_user.quiz # I'm assuming a user has one quiz?
#quiz.update(quiz_params) # Where quiz params takes the posted parameters from your update form
if #quiz.errors.any?
render :edit
else
redirect_to results_path
end
The key is you need to reassign #quiz with each request
Hi and thanks for taking the time to answer my question!
I have the following code in my api/ProjectController:
class Api::ProjectsController < ApplicationController
respond_to :json
def index
respond_with Project.all
end
def show
respond_with Project.find(params[:id])
end
def create
#project = Project.new(project_params)
if #project.save
redirect_to #project
else
render 'new'
end
end
end
I keep getting the following error:
undefined local variable or method `project_params' for #<Api::ProjectsController:0x00003d6d80b448>
When I change the second line in the create method to:
#project = Project.new(params[:project])
I get the following error:
ActiveModel::ForbiddenAttributesError
I am using ember and am sending JSON representation of the Project object from the front end. In both instances this is the json that's being propagated to the controller:
{"project"=>{"name"=>"asdfsdfsdf"}}
Can someone please spot where the mistake is.. I feel like I'm spending more time on this than I really should.. :/
Thank you so much and happy new year!!
You must define the project_params method that you're trying to use:
def project_params
params.require(:project).permit(:name)
end
You'll find more information and example on Rails 4 Strong parameters.
Looks like you don't have the project_params method defined in your Api::ProjectsController class. You need to add a method to your Api::ProjectsController called project_params like:
def project_params
params.require(:project).permit(:attribute)
end
Just replace the :attribute symbol with the actual attributes for the corresponding fields in your form. So if you have a 'name' and 'start_date' field for your project, then your require will look like params.require(:project).permit(:name, :start_date).