Default form values in Rails

One of the problems I initially ran into using Rails was the problem of how the hell you set initial values for your forms? (For example a select menu with a default value chosen but which a user can change). It turns out that as long as you don’t try and set the values in your views, it’s easy and there are several solutions.

As an example I started off with a classic Post model, then moved on to Ghost and Roast to try out alternatives (well you’ve got to call your models something…) I generated an identical scaffold for each and then ran a migration e.g.


rails generate scaffold Post name:string title:string type_id:integer

rake db:migrate


1. Use model after_initialize

/app/models/post.rb


class Post < ActiveRecord::Base
  attr_accessible :name, :title, :type_id
  validates :name,  :presence => true
  
  after_initialize :init
  
  private
    def init
      if self.new_record? && self.type_id.nil?
        self.type_id = 5
      end
    end

end

Edit the model and add an after_initialize callback. This callback is called after a new object is instantiated. The init method sets the default value, but only if the value has not been set yet.

http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

2. Set the default in the controller

/app/controllers/ghosts_controller.rb

  # GET /ghosts/new
  # GET /ghosts/new.json
  def new
    @ghost = Ghost.new

    respond_to do |format|
      format.html # new.html.erb
      format.json { render :json => @ghost }
    end
  end

Using this alternative solution, you simply set the default value in the new method using @ghost.type_id = 5. This value will only ever be set before the new view for this controller is created.

  # GET /ghosts/new
  # GET /ghosts/new.json
  def new
    @ghost = Ghost.new
    @ghost.type_id = 5

    respond_to do |format|
      format.html # new.html.erb
      format.json { render :json => @ghost }
    end
  end

This solution is easy implement and has the advantage that what you are doing is obvious – the default isn’t tucked away where you might forget about it.

3. Set the default in the database

When the rails generate command was run, a migration was automatically created:

/db/migrate/20130209234708_create_roasts.rb

class CreateRoasts < ActiveRecord::Migration
  def change
    create_table :roasts do |t|
      t.string :name
      t.string :title
      t.integer :type_id
      t.timestamps
    end
  end
end

We can edit the migration to set a default value here e.g.

class CreateRoasts < ActiveRecord::Migration
  def change
    create_table :roasts do |t|
      t.string :name
      t.string :title
      t.integer :type_id, :default => 5
      t.timestamps
    end
  end
end

Conclusion

Here are three ways of setting the default values. What you choose to do is up to you, I think it is important that a solution is consistent with the rest of the code that you are working with (as long as it isn't bad code). Maintainability and clarity shouldn't be neglected and I subscribe to the school of thought that you should always keep things simple - when complicated problems crop up, write complicated solutions, but don't complicate things for the sake of it.

Personally I would be disinclined to use the final method because I think it would be easy to forget about.

Creating a method in the model may be overkill most of the time, but if you are setting a lot of default values or your default values change with context, then setting the default values in the method makes sense to me.

These code examples were built with Ruby on Rails 3.2.9

Sinatra templates from params

Having been playing about with Sinatra I’m finding it cropping up in all kinds of useful ways. I shouldn’t be too surprised though, as that is the whole raison d’etre of micro-frameworks.

When I get around to it I am writing a little Sinatra app, but for me it has a found a real niche helping to prototype or mess around with CSS and Javascript – it only takes a few seconds to create a little app to play around with.

Here is a minimal app.rb file. It will load templates from the (default) /views directory based on the :name parameter.


require 'rubygems'
require 'sinatra'

get '/' do
    erb :index
end

get '/map/:name' do
    erb params[:name].to_sym
end

The key here is to_sym – this is not Sinatra specific it is part of the Ruby language. to_sym converts a string into a symbol.

If the concept of a symbol is a little woolly read this for an excellent explanation. An easy (although probably incorrect or simplistic) is to think of a ruby symbol simply as an immutable name for something that can either be a string or an integer. Once created it cannot change.