Lookup Tables in Rails
April 25, 2005
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!
April 25, 2005 at 3:19 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
April 25, 2005 at 3:19 pm
Just started Rails.
That was sweet mate, all worked straigt away.
Nice one, thanks.
April 25, 2005 at 3:19 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.