Skip to main content

Using Selection Lists in Rails

Posted by bleonard on July 10, 2007 at 7:17 AM PDT

Selection lists (drop down boxes) are common to just about every web application. In this entry I extend the web log I've been building to include a category selection list.

Setting Things Up

I'm going to begin from where I left off in my previous post: An Introduction to Using AJAX with Rails. Alternatively, you can start from, which is the completed project from that post.

Test the BlogDemo Project

  1. Open the BlogDemo project.
  2. Note, if you are starting with the provided BlogDemo project, you will also need to create the BlogDemo_development database and run the migrations.
  3. Run the project and browse to http://localhost:3000/blog to verify that it works.

The Plan

We're going to add the ability to assign our blog entries a category. We're going to do this by providing a list of categories to choose from when the entry is created. The list of available categories will be retrieved from a database table.

Step 1: Create the Categories Model

  1. Generate a new model named Category.
  2. Open CreateCategories and add the following to the self.up method which will create the categories table with the category string, create a category reference in the posts table (which will be required), and for good measure, define the foreign key relationship. Oh yeah, we also insert some test data.

      def self.up
        create_table :categories do |t|
          t.column 'category', :string

        add_column 'posts', 'category_id', :integer, {:null => false}
        execute "alter table posts add constraint fk_post_categories " <<
        "foreign key (category_id) references categories(id)"
        # Insert some test data...
        Category.create(:category => 'Java')
        Category.create(:category => 'Ruby')
        Category.create(:category => 'JavaScript')
        Category.create(:category => 'NetBeans')
        Category.create(:category => 'Personal')

  3. For completeness, add the following to the self.down method:

      def self.down
        execute "alter table posts drop foreign key fk_post_categories"
        remove_column 'posts', 'category_id'
        drop_table :categories

  4. Right-click the project and select Migrate Database > To Current Version:

  5. If you try to create a new blog entry now, it will fail because category_id can't be null:

Add the Category Select List

  1. Alt+Shift+O to open the _form.rhtml partial.
  2. Here we'll use the select helper to create our drop down box. Above the post title in _form.rhtml, add the following:

    <%= select 'post', 'category_id',
        Category.find(:all).collect {|c| [c.category,]},
        :prompt => "Select a Category" %>

    'post' and 'category_id' map to the post model just like the title and body fields. The 3rd parameter:

    Category.find(:all).collect {|c| [c.category,]},
    returns a collection of all the categories (for display to the user) and their associated ids (for storage in the post tables category_id field). The final parameter:

    :prompt => "Select a Category" %>
    Just set's prompt instructing the user to select a category.

  3. Now we should be able to create an entry if we select a category:

  4. But if you fail to select a category, we get the same 'category_id' cannot be null error.

Validate Presence

  1. Alt+Shift+O to open Post.
  2. Type vp+tab to insert the validates_presence_of template and set the :attribute to :category_id

    validates_presence_of :category_id
  3. Save and try to insert the post again without a category selected:

Display the Selected Category

As a final step we're going to update the show page to display the post's category.

  1. Alt+Shift+0 to open show.rhtml.
  2. Replace the for loop at the top of the file with the following:

    Category: <%= @post.category.category %>

    Title:    <%= @post.title %>

    Body:     <%= @post.body %>

  3. Save and browse to the show page:

    Although we set up the foreign key relationship between posts and categories in our database, Rails knows nothing about it.

  4. Switch to post.rb and add the following:

    belongs_to :category
    And try again:


The Completed Application

Related Topics >>