Type a keyword and hit enter to start searching. Press Esc to cancel.

Currently Reading

Ruby on Rails: Associations With Multiple Foreign Keys

The perfect database schema is one where you don’t need to concede form to any obscure requirements. In that perfect scenario all associated models could be individually identified with a single foreign key.   According to the Rails guides, you can only define an association’s foreign key with a single column.

We live in an imperfect world with many obscure business requirements, so sometimes we need to use two keys.  One common scenario is when you need to centralize and synchronize data from multiple outside API’s.  Luckily, there is a solution.

This example will be based on an Entity Attribute Value (EAV) mapping scenario.

  • Entity and EntityAttribute models, which are joined with an EntityAttributeValue model.
  • EntityAttribute represent different data types, so there is a EntityAttributeValue#value method which casts the stored value to the correct data type.
  •  In specific cases, the actual value must be selected from a list of EntityAttributeOption.

In order to identify all Entity records that are associated with EntityAttributeOption, we need to select the EntityAttributeValue records that have the same EntityAttribute and value as the EntityAttributeOption.

class EntityAttributeValue < ActiveRecord::Base

belongs_to :entity
belongs_to :entity_attribute
belongs_to :entity_attribute_option,
       ->(join_or_model) {
       if join_or_model.is_a? EntityAttributeValue
         where(entity_attribute_id: join_or_model.entity_attribute_id)
       else
         where('entity_attribute_options.entity_attribute_id = entity_attribute_values.entity_attribute_id')
       end
       },
       foreign_key: 'value',
       primary_key: 'value'
end
class EntityAttributeOption < ActiveRecord::Base
  belongs_to :entity_attribute

  has_many :entity_attribute_values,
     Proc.new { |join_or_model|
       if join_or_model.is_a? EntityAttributeOption
        where(entity_attribute_id: join_or_model.entity_attribute_id)
       else
        where('entity_attribute_values.entity_attribute_id = entity_attribute_options.entity_attribute_id')
       end
     },
     foreign_key: 'value',
     primary_key: 'value'
end

To accomplish an association with multiple foreign keys, a dynamic scope is used to determine the direction of the association and apply different queries to identify associated records using shared the EntityAttribute and the dynamic value.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *