Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Ruby

chris salvi
chris salvi
7,584 Points

Upgrade to rails 4.x for treebook went flawless until I hit one minor snag

When I login and try to post a new status, I get the error message screen telling me the following:

ActiveModel::ForbiddenAttributesError

Now I assume this must be something new to 4.0 as I followed the video carefully and double checked my create method in my status_controller. I will post a screenshot for assurance.

Anyone know what I need to do here so I can create posts again in the upgrade app?

http://postimg.org/image/jv2icsq23/

3 Answers

Matt West
Matt West
14,545 Points

Yep, you will need to alter the update line as you're doing mass assignment here (altering multiple attributes by passing an object).

@status.update(status_params)

That set_status method is a nice design pattern to use.
Make sure you have a before_filter set up that calls the method before the update action.
You then can remove the first line of your update action.

One note here is that you will need to change the code in your set_status method to make sure that nobody can alter other people's statuses. Just use the first line of your current update action instead.

# TODO: Add more actions in the array here.
# Anywhere that you need to get the status by ID (i.e. `edit`).
before_filter :set_status, only: [:update]


private

  def set_status
    @status = current_user.statuses.find(params[:id])
  end

You can also delete the following if you make sure that user_id is not listed in your permitted parameters.

if params[:status] && params[:status].has_key?(:user_id)
  params[:status].delete(:user_id)
end
chris salvi
chris salvi
7,584 Points

Matt West

Hey mate, running into issues again. When I try to post a new status I now receive this error. Boy this is rough.... Thanks for all your help

http://postimg.org/image/odewt8jn9/

Matt West
Matt West
14,545 Points

Hey Chris,

What does your Status model look like? Please post the code and I'll take a look.

chris salvi
chris salvi
7,584 Points
<p id="notice"><%= notice %></p>

<p>
  <strong>Name:</strong>
  <%= @status.full_name %>
</p>

<p>
  <strong>Content:</strong>
  <%= @status.content %>
</p>

<%= link_to 'Edit', edit_status_path(@status) %> |
<%= link_to 'Back', statuses_path %>
Matt West
Matt West
14,545 Points

Sorry, I meant the status.rb file in the app/models folder.

Do you have your code hosted on GitHub? That might be easier than having to copy and paster everything :)

Matt West
Matt West
14,545 Points

Thanks Chris,

Try the following:

<p>
  <strong>Name:</strong>
  <%= @status.user.full_name %>
</p>

full_name is a method on the User model so you'll need to call it on the owner of the status, not the status itself. (i.e. @status.user)

chris salvi
chris salvi
7,584 Points

Sorry, that is what I originally had before messing around with it, but it still throws the same error message that I listed before :(

Matt West
Matt West
14,545 Points

That's strange, I just cloned the repo to my computer and made the change. It fixed the error.

I've submitted a pull request on Github. Merge it into your repo and give it a go.

chris salvi
chris salvi
7,584 Points

ok will do when I get home from work. Thanks for helping me out btw. You are very kind and part of what makes this community great!

Matt West
Matt West
14,545 Points

No problem :)

chris salvi
chris salvi
7,584 Points

Thanks matt, youre the best.

One last question, I have tried to mess around with the CSS to make my links at the top of the page on the nav bar inline, but havent been able to do so yet. In theory this should be easy, but Im guessing it's because Im using a more modern version of bootstrap than the videos. Do you see any suggestions I can make to fix this?

Obrigado!

Matt West
Matt West
14,545 Points

Hi Chris,

I've made some alterations to my fork on Github that I think will get things looking how you'd like. Take a look at the updated application.html.erb file: https://github.com/matt-west/treebook/blob/master/app/views/layouts/application.html.erb

And the update application.css: https://github.com/matt-west/treebook/blob/master/app/assets/stylesheets/application.css

Let me know if you have any queries about the alterations.

Matt West
Matt West
14,545 Points

Hi Chris,

Rails 4 introduces some changes to how parameters are handled in controllers, specifically when it comes to mass assignments like you're doing here in your create action.

Whereas before attributes were secured in the model using attr_accessible, rails 4 now secures parameters in the controller.
This new feature is called strong parameters. You can read about it here: http://guides.rubyonrails.org/v4.0.8/action_controller_overview.html#strong-parameters

You now need to whitelist parameters used in mass assignments before they can be used in your controller. This change helps to prevent unwanted parameters from making their way into your database queries, making your rails app more secure.

There's two key changes you'll need to make to your code.

First, create a private status_params method that defines the permitted parameters. See the code below for an example. Make sure you update the parameters listed in the permit method to match the attributes of the Status model you need to change.

Second you need to update your create action to use status_params instead of params[:status] when creating the new status.

Here's a modified example of your code. I don't have the treebook project to hand, so apologies if any of this is off. It should however give you an idea of where you need to make changes.

class StatusController < ActionController::Base

  def create
    # You now use status_params instead of params[:status]
    @status = current_user.statuses.new(status_params)

    # The rest of your code in the create action is just fine :)
  end

  # More controller actions in here...

  private

    def status_params
      # TODO: Change the parameters listed within `permit` to match the Status attributes you need to change.
      params.require(:status).permit(:name, :age)
    end
end

Post your full status_controller.rb file if you encounter any issues and I'll do my best to help out :)

chris salvi
chris salvi
7,584 Points

you are my ruby savior, Matt. I have had some difficulty with the new strong params requirement in 4.0, but I think Im finally getting a better grasp on it with that link above. Thanks!

Matt West
Matt West
14,545 Points

Good to hear!

Strong params threw me out a bit at first too. It makes total sense once you get used to it though :)

chris salvi
chris salvi
7,584 Points

One last question Matt West

Here is my update status: http://postimg.org/image/w9v7p0txp/

do I need to change the instance @status here as well, and do I need to alter the conditional statement below it as well?

chris salvi
chris salvi
7,584 Points

I have this method along with status_params under private methods

def set_status @status = Status.find(params[:id]) end

chris salvi
chris salvi
7,584 Points
class StatusesController < ApplicationController
  before_action :set_status, only: [:show, :edit, :update, :destroy]
  before_filter :authenticate_user!, only: [:new, :create, :edit, :update]

  # GET /statuses
  # GET /statuses.json
  def index
    @statuses = Status.all
  end

  # GET /statuses/1
  # GET /statuses/1.json
  def show
  end

  # GET /statuses/new
  def new
    @status = Status.new
  end

  # GET /statuses/1/edit
  def edit
  end

  # POST /statuses
  # POST /statuses.json
  def create
        @status = current_user.statuses.new(status_params)

    respond_to do |format|
      if @status.save
        format.html { redirect_to @status, notice: 'Status was successfully created.' }
        format.json { render :show, status: :created, location: @status }
      else
        format.html { render :new }
        format.json { render json: @status.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /statuses/1
  # PATCH/PUT /statuses/1.json
  def update
    @status.update(status_params)
    if params[:status] && params[:status].has_key?(:user_id)
       params[:status].delete(:user_id) 
    end

    respond_to do |format|
      if @status.update(params[:status])
        format.html { redirect_to @status, notice: 'Status was successfully updated.' }
        format.json { render :show, status: :ok, location: @status }
      else
        format.html { render :edit }
        format.json { render json: @status.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /statuses/1
  # DELETE /statuses/1.json
  def destroy
    @status.destroy
    respond_to do |format|
      format.html { redirect_to statuses_url, notice: 'Status was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private

    def set_status
        @status = current_user.statuses.find(params[:id])
    end

    def status_params
      params.require(:status).permit(:user_id, :content) 
    end
end