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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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
1 2 3 4 5 6 7 8 9 10 | # 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.
1 2 3 4 5 6 7 8 9 10 11 | # 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
1 2 3 4 5 6 7 8 9 10 | 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.
1 2 3 4 5 6 7 8 9 10 | 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