Logo Questions Linux Laravel Mysql Ubuntu Git Menu

Rails ActiveRecord handle an id column that is not the primary key

Struggling with ActiveRecord auto assigning the :id attribute as the primary key even though it is a separate column.

Table - legacy-table

    id - int
    pk_id - int (primary key)
    name - varchar2
    info - varchar2


class LegacyModel < ActiveRecord::Base
  self.table_name = 'legacy-table'
  self.primary_key = 'pk_id'
  default_scope {order(:name => :asc)}
  alias_attribute :other_id, :id


I don't care that ActiveRecord automatically assigns the primary key (pk_id) to the :id attribute however I lose all access to the actual id column. Trying to use the alias simply points me back at the primary key.

However one caveat to this issues is that from the view i can access the id column by using @legacymodel[:id]. But again when calling @legacymodel.id I get the value of the pk_id column. What i want is to be able to call @legacymodel.other_id and have it point to the id column. Instead @legacymodel.service_id, @legacymodel.id, and @legacymodel.pk_id all point to the same column pk_id

Please note that this is a legacy db and modifying the columns are out of the question. I am using Rails 4 with MySql.

Is there anyway to code around this? Why does @legacymodel[:id] give me different results then @legacymodel.id?

like image 221
Matt Avatar asked Mar 20 '23 18:03


1 Answers

The answer by @cschroed did not work for me in the latest Rails (v4.2). Digging into the Rails source code, it appears that read_attribute will also use the primary key value if the key passed equals 'id':

  ID = 'id'.freeze

  # Returns the value of the attribute identified by <tt>attr_name</tt> after
  # it has been typecast (for example, "2004-12-12" in a date column is cast
  # to a date object, like Date.new(2004, 12, 12)).
  def read_attribute(attr_name, &block)
    name = attr_name.to_s
    name = self.class.primary_key if name == ID
    _read_attribute(name, &block)


Since, the [] method uses read_attribute, this no longer works.

I found that directly reading from the attributes hash worked instead:

# LegacyModel class
def other_id

This provided a means of bypassing read_attribute by mimicking _read_attribute.

like image 169
cweston Avatar answered Apr 26 '23 13:04