Creating inheritance records for individual tables in Rails
I am using unidirectional inheritance for my application. My polymorphic type is a service with only one subtype called OilChange. I am having problems creating my entries in my create method in a controller. Here is the code.
@log = Log.new(params[:log])
@log.maintenance = Maintenance.new(params[:maintenance])
The params [: maintenance] hash has keys {: name ,: type}. I can check their existence and values by printing them like this:
print params[:maintenance][:name]
print params[:maintenance][:type]
If I go to "OilChange" for a key value of type:, the maintenance record is of type Maintenance, not OilChange. I can check by finding the entry in the REPL console. Field of type nil. I can get it to work as I want by adding the following line.
@log.maintenance.type = params[:maintenance][:type]
But this is ugly. I am wondering why the create method does not set the type field as it just finds the name?
The two types you see look like this in my schema.rb schema
create_table "logs", :force => true do |t|
t.date "date"
t.text "description"
t.string "title"
t.string "summary"
t.integer "car_id"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "maintenance_id"
t.integer "mileage"
end
create_table "maintenances", :force => true do |t|
t.string "name"
t.string "type"
t.datetime "created_at"
t.datetime "updated_at"
t.string "oil_brand"
t.string "oil_type"
t.string "oil_filter_type"
My models look like this.
class Log < ActiveRecord::Base
belongs_to :car
has_and_belongs_to_many :tags
belongs_to :maintenance
end
class Maintenance < ActiveRecord::Base
has_one :log
end
class OilChange < Maintenance
end
TIA!
source to share
The concrete answer is that the attribute type
, like many Rails special attributes, is protected from mass assignment. (Look :attr_protected
in the documentation.)
The answer to a more general problem is that you don't trust your models enough. If you want to create a record of type OilChange, you do not need to name Maintenance.new
or Maintenance.create
. Call OilChange.new
or OilChange.create
instead and Rails will automatically take care of setting up the type and doing all the background work for you.
source to share
Try using the following code:
begin
klass = Module.const_get(params[:maintenance][:type])
@log.maintenance = klass.new(params[:maintenance])
if ! @log.maintenance.is_a?(Maintenance)
@log.maintenance = Maintenance.new(params[:maintenance])
end
rescue NameError
@log.maintenance = Maintenance.new(params[:maintenance])
end
If you only want to have Maintenance subclasses in your database, follow these steps in your Maintenance class:
class Maintenance < ActiveRecord::Base
validates_presence_of :type
# other stuff goes here
end
This code will create an invalid service class if the parameters are of the wrong type. If they contain the correct type, an instance of the specified class will be created.
If your models use namespaces, you can look at this article instead of using Module.const_get
:
source to share