Changing Type of Activerecord Class in Rails with Single Table Inheritance

Changing type of ActiveRecord Class in Rails with Single Table Inheritance

You can just set the type field to 'User' and save the record. The in-memory object will still show as a BaseUser but the next time you reload the in-memory object will be a User

>> b=BaseUser.new
>> b.class # = BaseUser

# Set the Type. In-Memory object is still a BaseUser
>> b.type='User'
>> b.class # = BaseUser
>> b.save

# Retrieve the records through both models (Each has the same class)

>> User.find(1).class # = User
>> BaseUser.find(1).class # User

Rename single table inheritance rails model class name without changing existing data

This is how you could do it:

  1. Create ULD < Component component/file that is a copy of Uld < Component. I.e. have 2 components, don't remove Uld
  2. In places where you previously fetched Uld make it so that now you fetch records where type is Uld or ULD
  3. In places where you created new Uld records make it so that it instead creates new ULD records
  4. Deploy these changes without any migrations, this way your servers will be backwards compatible with Uld but would also work with ULD
  5. Deploy your migration now
  6. Finally, remove all the mentions/files related to the old Uld and deploy again

Rails 4 update Type when migrating to Single Table Inheritance

If you had more rows in the database User.each would become quite slow as it makes an SQL call for each user.

Generally you could use User.update_all(field: value) to do this in one SQL call but there is another reason to avoid this: if the User model is later removed the migration will no longer run.

One way to update all rows at once without referencing the model is to use raw SQL in the migration:

def up
execute "UPDATE users SET type = 'Admin' WHERE type IS NULL"
end

Rails / ActiveRecord - Single Table Inheritance - overriding type field

In modern Rails, you'd use inheritance_column= (as panckreous noted):

class M < ApplicationRecord
self.inheritance_column = 'whatever'
#...
end

In older versions of Rails (i.e. what was around when this answer was originally written), you'd use [set_inheritance_column] to change the name:

Sets the name of the inheritance column to use to the given value, or (if the value is nil or false) to the value returned by the given block.

The column still has to be a string (or text) as AR will want to put the class name in there:

Single table inheritance

Active Record allows inheritance by storing the name of the class in a column that is named “type” by default.

Single Table Inheritance - Additional Class Name

After detailed research and diving deep into the Rails STI source code, I found that my scenario will need to override the Rails' default STI. Following is what I needed to achieve the goal:


class Candidate::Site

# To enable renaming of Site to Candidate::Site
# per ENG-9551, we need to:
# 1. disable the 'type' column from using Rails' built-in STI
self.inheritance_column = :_nonexistant_column_to_bypass_sti

# 2. manually set the type column to Candidate::Site when we save,
# so that new and edited records are saved as Candidate::Site
before_save { self.type = 'Candidate::Site' }

# 3. always report the type as a Candidate::Site
def type
'Candidate::Site'
end

# 4. and set a default scope which only reads type columns that are
# 'Candidate::Site' or 'Site' so that STI
# still appears to be the same as always
default_scope { where(type: 'Candidate::Site').or(where(type: 'Site')) }

...
...
...
end

So, now any new record created with Candidate::Site.create will store type Candidate::Site while queries will use default scope and consider both the type, Candidate::Site as well as Site.



Related Topics



Leave a reply



Submit