life.i.think: Lookup Tables in Rails

Lookup Tables in Rails
Scribbled on April 25th. 3 comments.

So look up tables are fun. Well, maybe not fun, but they are good for database integrity and all that jive. But how to do them cleanly in Rails?

Firstly, lets talk about database schemas. There are 2 primary ways to do lookup tables. One is to have a lookup table for each field. You end up with something like:

  lookup_genders
  -------------
  int id
  varchar description

  lookup_states
  -----------
  int id
  varchar description

This is a bit messy, as you can end up with a gazillion of these suckers in your database. Now, if you aren’t a normal form nazi, you can break the rules a bit and do something like:

  lookups
  -------
  int id
  varchar description
  varchar field_name

Now, this is populated with data like:

  [1, 'Male', 'gender']
  [2, 'Female', 'gender']
  [3, 'KY', 'states']

I’m using the latter for my rails app. Now when displaying the data in pure text form, I want the value of the id to be translated into its cooresponding description. So in the rails model that contains a lookup, I override the method to return that field with this (I’ll use gender as my example from here on):

  # for reading
  def gender
    # first we get the name of the function we're operating within.
    # this is done with the ruby Kernel#caller() method.
    db_field = caller(0)[0].match(/`(.*)'/)[1]

    @lookup = Lookup.find(:all, :conditions  => [ "field_name = ?", db_field])
    for i in @lookup
      if self[db_field] == i.id
        return i.description
      end
    end
    return 'lookup error!'
  end

Lookup.find just searches through our lookup table (it’s an empty model in rails).

Cool right? Now what about putting the data back in the database? Well the best way to deal with lookup data is with a dropdown. So I have the following.

In /app/helps/application_helper.rb:

  def lookup_dropdown(parent_record, lookup_field)
    name = parent_record + "[" + lookup_field + "]" 
    lookup = Lookup.find(:all, :conditions  => [ "field_name = ?", lookup_field])
    render_partial("shared/lookup_dropdown", {:name => name, :values => lookup})
  end

In /app/views/shared, add the file _lookup_dropdown.rhtml with the following contents:

  <select name=<%= name %>>
    <% for i in values %>
      <option value="<%= i.id %>"><%= i.description %></option>
    <% end %>
  </select>

Now, anywhere where you want a dropdown for this lookup/field you can use:

<%= lookup_dropdown "inmate", "gender" %>

Tada!

Comments

Leave a response

  1. mark.torrens@tiscali.co.ukFebruary 07, 2006 @ 08:11 AM

    Just started Rails.

    That was sweet mate, all worked straigt away.

    Nice one, thanks.

  2. John DevineAugust 03, 2007 @ 03:24 PM

    Great idea and piece of code! Thanks heaps.

    I have modified it to be able to handle editing (needed a default = to current value) and added a field to allow ordering of the entries.

  3. Dan SharpJanuary 21, 2008 @ 11:48 PM

    Thanks for this!

    I need to do something very similar, except in my case, the actual lookup table’s contents will be built at run time, rather than before… i.e. by users. Users can create a new category (like “gender”) and then populate it with the lookup values (i.e. “Male”, “Female”).

    I wonder if your “def gender…” block is “abstractable”. I think it can be done, but I’m trying to figure out the best way. Plus, in a Rails life, I’d love to have automatic getter/setter methods that are treated as attributes on some model. I think your stuff, combined with something like this: http://redcorundum.blogspot.com/2006/07/ror-additional-attributes-with-sti.html might actually get me there.

    Thanks again!

    -Dan

Comment