Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Ruby

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

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

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/

Hey Chris,

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

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

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 :)

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)

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

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.

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!

No problem :)

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!

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.

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 :)

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!

Good to hear!

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

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?

I have this method along with status_params under private methods

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

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