views:

245

answers:

3

You know how some bug trackers (and other software) allow you to add custom fields?

Typically this is done with a data structure that looks something like this:

  Items
----------
 ID | NAME | ITEM_TYPE_ID


 FieldDefinitions
---------------------------------------
 ID | ITEM_TYPE_ID | FIELD_NAME | FIELD_TYPE

 FieldValues
---------------------------------------
ID | FIELD_ID | ITEM_ID | VALUE

I'm trying to figure out the best way to approach this design in Rails. There will be many models which I want to allow extending simple properties.

When I retreive an Item I'd like it to include a hash of the addition field values that have been defined for that model.

+1  A: 

Something like...?

class Item < ActiveRecord::Base
  has_many :field_values
  has_many :field_definitions, :through => :field_values

  def custom_fields_hash
    cfh = {}
    self.field_values.each |fv|
      cfh[fv.field_definition] = fv
    end
    cfh
  end
end

class FieldValue < ActiveRecord::Base
  belongs_to :item
  belongs_to :field_definition
end

class FieldDefinition < ActiveRecord::Base
  has_many :field_values
  has_many :items, :through => field_values
end

Or, you could change

cfh[fv.field_definition] = fv

...to...

cfh[fv.field_definition.field_name] = fv.value
fd
Using inject would probably be better form. Like self.field_values.inject({}) { |memo, fv| memo[fv.field_definition] = fv } or similar.
fd
This is what I was thinking I'd use... was wondering if there was a more "ruby" way to do this...
Ben Scheirman
The usual way would probably not to use a hash at all and just use the generated association methods, but I guess there's a reason why you were particularly wanted a hash...?
fd
I was thinking of person.meta[:twitter] or something of the sort. Feel free to educate me on something cleaner :)
Ben Scheirman
+1  A: 

Take a look at the Friendly ORM. It allows you to work with mysql without a schema. It was inspired by this blog post about how Friendfeed uses mysql to store schemaless data.

Friendly would replace ActiveRecord, although you can use ActiveRecord models next to Friendly models pretty easily.

Not exactly what I'm looking for, but a cool find nonetheless. +1
Ben Scheirman
If you do consider schema-less data, you might want to look at using a document database like CouchDB or MongoDB (see http://railscasts.com/episodes/194-mongodb-and-mongomapper for a webcast about using MongoDB and the MongoMapper gem in a rails app; Ryan Bates is awesome btw).
fd
Yeah, I'm definitely interested in that. I'm on Heroku, which currently won't work with MongoDB.
Ben Scheirman
+1  A: 

If your intention is to have your FieldDefinition records belonging to different models (for example, a column item_id that points to either a SqueekyToyItem or a BalloonItem) then what you want is a polymorphic association.

It basically allows you to have an item_type column (beside your item_id column), which then specifies the actual type of item it points to.

There's a heading “Polymorphic associations” in the documentation for ActiveRecord::Associations::ClassMethods.

Shtééf
I do intend to have them on many models, but they will all inherit from Item (or whatever my base class ends up being)
Ben Scheirman