Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I Create a Rails Model from a Subset of Table Records

I am trying to create several models that all pull from the same table. How do I limit the table records in each model? And before you tell me to change my data structure, this is a reporting application that is pulling from a preexisting backing DB over which I have no control.

My table looks something like:

Vehicle_Table
id vehicle_type name
--------------------
1  Car          Foo
2  Car          Bar
3  Motorcycle   Baz
4  Car          Barf

And I want to build models for Car and Motorcycle like:

class Car < ActiveRecord::Base
  set_table_name 'Vehicle_Table'

end

and

class Motorcycle < ActiveRecord::Base
  set_table_name 'Vehicle_Table'

end

But I have no idea how to say, "Hey Active Record, I only want records where vehicle_type = motorcycle in the motorcycle model."

I'm sure this is friggin' obvious, but all of my Google searches return ways to FIND subsets in a model rather than RESTRICT a model to a specific subset of records.

like image 979
Bruce P. Henry Avatar asked Feb 21 '23 11:02

Bruce P. Henry


2 Answers

This is called Single Table Inheritance (STI).

If you had a column named type in your table, it would likely work automatically. But you can change this column name that Rails uses to tell types apart.

http://api.rubyonrails.org/classes/ActiveRecord/Base.html

Single table inheritance

Active Record allows inheritance by storing the name of the class in a column that by default is named “type” (can be changed by overwriting Base.inheritance_column). This means that an inheritance looking like this:

class Company < ActiveRecord::Base; end
class Firm < Company; end
class Client < Company; end
class PriorityClient < Client; end

When you do Firm.create(:name => "37signals"), this record will be saved in the companies table with type = “Firm”. You can then fetch this row again using Company.where(:name => '37signals').first and it will return a Firm object.

So, try this code

class Car < ActiveRecord::Base
  set_table_name 'Vehicle_Table'
  self.inheritance_column = :vehicle_type
end
like image 75
Sergio Tulentsev Avatar answered Feb 24 '23 00:02

Sergio Tulentsev


Commented above but had limited editing abilities. I came across this exact problem and found the second half of the solution elsewhere. STI will allow you to get a subset of a table based on a column in the table but it will key off of the class name to find the records for that class. For example:

class Company < ActiveRecord::Base; end    
class Client < Company; end

This will look at the table named Company for records that have the value 'Client' in a column named 'Type'.

You can override the column that STI checks by doing

class Company < ActiveRecord::Base
  self.inheritance_column = :company_type
end

But it still looks for that column to contain 'Client'. You can override what value it looks for by doing this:

class Client < Company
  def self.sti_name
    1
  end
end

This will now look at the company_type column for rows with a value of 1.

For Rails-4.2 this is nearly identical but does not need a class method:

  private

  self.inheritance_column = :company_type

  def sti_name
    1
  end
like image 45
BaroldGene Avatar answered Feb 24 '23 02:02

BaroldGene