Changing Table Name at Query Run Time in a Rails Application

Changing table name at query run time in a Rails application

The method

def self.for_tenant(tid)
self.table_name = "products_" + tid.to_s
self
end

makes sense, however, it has a side effect: it changes table name for Product class. When this class is used later in the same request, for example, in this way:

Product.where(name: "My other product") ...

the table name won't be products as you may expect; it will stay the same as changed by for_tenant method previously.

To avoid this ambiguity and keep code clean you can use another strategy:

1) Define a module which holds all logic of work with tenant partitions:

# app/models/concerns/partitionable.rb

module Partitionable
def self.included(base)
base.class_eval do
def self.tenant_model(tid)
partition_suffix = "_#{tid}"

table = "#{table_name}#{partition_suffix}"

exists = connection.select_one("SELECT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'public' AND tablename = '#{table}')")
unless exists['exists'] == 't' || exists['exists'] == true # different versions of pg gem give different answers
return self # returning original model class
end

class_name = "#{name}#{partition_suffix}"

model_class = Class.new(self)

model_class.define_singleton_method(:table_name) do
table
end

model_class.define_singleton_method(:name) do
class_name
end

model_class
end
end
end
end

2) Include this module in your model class:

class Product < PostgresDatabase
include Partitionable

...
end

3) Use it the same way as you intended:

Product.tenant_model(TENANT_ID).where(name: "My product")...

What's happened there:

Method tenant_model(TENANT_ID) creates another model class for the tenant with ID TENANT_ID. This class has name Product_<TENANT_ID>, works with table products_<TENANT_ID> and inherits all methods of Product class. So it could be used like a regular model. And class Product itself remains untouched: its table_name is still products.

How to change ActiveRecord's table name during runtime

I found the explanation for the described behavior. Although reset_table_name resets the table name computed from the prefix, suffix (and maybe other things too), the table is initialized when a model is used and a query is generated. ActiveRecord works "on the top of" Arel, a relational algebra gem. When an ActiveRecord model is used a table is created and filled @arel_table instance variable. This caching is for performance purposes. If one wants to recreate the arel table, it can be reset by calling reset_column_information. I needed to have both reset_table_name and reset_column_information in order to get a new table for the new table name. Probably I will have to worry about the performance, if I reset the table often.

Rails 3/ActiveRecord: How to switch/change table name during request dynamically?

I would use class variables:

class SampleData < ActiveRecord::Base
class << self
@@model_class = {}

# Create derived class for id
@@model_class[id] = Class.new(SampleData)
@@model_class[id].set_table_name "sample_data_#{id}"
# Repeat for other IDs
end
end

Now you can use the derived classes over and over, without causing memory leaks.

Depending on your actual situation (eg you do not know the IDs on beforehand), you could check whether the id is already present in the Hash dynamically and add it if not.

How to dynamically set model's table_name?

What I realized is, that I could just use superclass without using globals, which I ended up using. This does not pose a race condition issue as with globals.

class ListenLocB < ListenLoc
superclass.table_name = 'LISTEN_ADDR_B' # or ListenLoc.table_name
self.sequence_name = :autogenerated
belongs_to :category, class_name: 'ListenLocCatB', foreign_key: 'cat_id'
belongs_to :country, class_name: 'Country', foreign_key: 'country_id'
end

Renaming table in rails

You would typically do this sort of thing in a migration:

class RenameFoo < ActiveRecord::Migration
def self.up
rename_table :foo, :bar
end

def self.down
rename_table :bar, :foo
end
end

How to re-name a ActiveRecord Model which can automatically change the table name in DB?

I would recommend the following:

  1. Change manually the Active Record model class into Train

  2. Make migration to change the database table name from cars to trains

  3. Make good search to change references from Car to Train.

If you have constantly the need to change database table names, you might want to reconsider naming the tables more abstact way. Like in this case you could have table called vehicles and have the "type" field specifying the type (for instance: car or train).

How can I rename a database column in a Ruby on Rails migration?

rename_column :table, :old_column, :new_column

You'll probably want to create a separate migration to do this. (Rename FixColumnName as you will.):

bin/rails generate migration FixColumnName
# creates db/migrate/xxxxxxxxxx_fix_column_name.rb

Then edit the migration to do your will:

# db/migrate/xxxxxxxxxx_fix_column_name.rb
class FixColumnName < ActiveRecord::Migration
def self.up
rename_column :table_name, :old_column, :new_column
end

def self.down
# rename back if you need or do something else or do nothing
end
end

For Rails 3.1 use:

While, the up and down methods still apply, Rails 3.1 receives a change method that "knows how to migrate your database and reverse it when the migration is rolled back without the need to write a separate down method".

See "Active Record Migrations" for more information.

rails g migration FixColumnName

class FixColumnName < ActiveRecord::Migration
def change
rename_column :table_name, :old_column, :new_column
end
end

If you happen to have a whole bunch of columns to rename, or something that would have required repeating the table name over and over again:

rename_column :table_name, :old_column1, :new_column1
rename_column :table_name, :old_column2, :new_column2
...

You could use change_table to keep things a little neater:

class FixColumnNames < ActiveRecord::Migration
def change
change_table :table_name do |t|
t.rename :old_column1, :new_column1
t.rename :old_column2, :new_column2
...
end
end
end

Then just db:migrate as usual or however you go about your business.


For Rails 4:

While creating a Migration for renaming a column, Rails 4 generates a change method instead of up and down as mentioned in the above section. The generated change method is:

$ > rails g migration ChangeColumnName

which will create a migration file similar to:

class ChangeColumnName < ActiveRecord::Migration
def change
rename_column :table_name, :old_column, :new_column
end
end

ActiveRecord wrong table_name

Media is a already a plural (it's the plural of medium), so active record pluralizes no further, even if people say "medias" often enough.

You can check how rails pluralizes things from the rails console

"medium".pluralize #=> "media"
"media".pluralize #=> "media"

If you disagree strongly and don't want to rename your table / model, you can either force active record to use your table name

class Media < ActiveRecord::Base
self.table_name = "medias"
end

or customise the inflections. Your app should have a config/initializers/inflections.rb which sets out how to do this.



Related Topics



Leave a reply



Submit