Forms and strong parameters

Ruby on Rails · Essentials · 14 min

Forms are how users change data, and strong parameters are Rails' guard against mass-assignment attacks.

form_with(model: @article) builds a form bound to a model. Rails picks the URL and method by convention: POST to /articles for a new record, PATCH to /articles/:id for an existing one. Inside the block, field helpers name inputs article[title] and pre-fill current values:

<%= form_with model: @article do |f| %>
  <%= f.label :title %>
  <%= f.text_field :title %>
  <%= f.text_area :body %>
  <%= f.submit %>
<% end %>

Other helpers: f.select, f.check_box, f.date_field, f.email_field, f.file_field, f.collection_select. The form includes a hidden CSRF token, which Rails verifies on every non-GET request.

Display validation errors with @article.errors.full_messages, or per field with @article.errors[:title]. Invalid fields are wrapped in a field_with_errors div for styling.

The submitted data arrives nested: params[:article][:title]. Passing raw params to create or update would let an attacker set any attribute, such as admin: true, so Rails refuses unpermitted params. Strong parameters whitelist what's allowed. Rails 8 adds params.expect, which also checks the shape and returns a 400 for malformed input:

params.expect(article: [:title, :body, tag_ids: []])

The older equivalent is params.require(:article).permit(:title, :body, tag_ids: []). Arrays and nested hashes must be declared explicitly.

Example

# app/controllers/articles_controller.rb
def new
  @article = Article.new
end

def create
  @article = Current.user.articles.new(article_params)
  if @article.save
    redirect_to @article, notice: "Published!"
  else
    render :new, status: :unprocessable_entity
  end
end

private
def article_params
  # Rails 8: whitelist and check the shape in one call
  params.expect(article: [:title, :body, :category_id, tag_ids: []])
end
# Submitted: { article: { title: "Hi", body: "...", admin: "1" } }
# -> admin is filtered out; a non-hash article param -> 400 Bad Request
# app/views/articles/_form.html.erb (excerpt)
# <% if article.errors.any? %>
#   <ul><% article.errors.full_messages.each do |m| %><li><%= m %></li><% end %></ul>
# <% end %>

Remember

  • form_with(model:) picks the URL, method and field values by convention
  • Strong parameters whitelist attributes before mass assignment
  • params.expect (Rails 8) checks shape too and returns 400 for bad input

Watch out for

  • Forgetting to permit array params like tag_ids: [], so they are silently dropped.

Check yourself

  1. Why does Article.create(params[:article]) raise ForbiddenAttributesError?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: guides.rubyonrails.org/form_helpers.html