开发者

Creating Single Table Inheritance Records in Rails

I'm using single table inheritance for my application. My poly开发者_如何学编程morphic type is Maintenance with only one subtype, right now, named OilChange. I'm running into problems creating my records in my create method in the controller. Here's the code.

@log = Log.new(params[:log])
@log.maintenance = Maintenance.new(params[:maintenance])

The params[:maintenance] hash has keys {:name, :type}. I can verify their existence and values by printing them out as follows

print params[:maintenance][:name]
print params[:maintenance][:type]

If I pass in "OilChange" for the value of the :type key, the Maintenance record is of type Maintenance and not of OilChange. I can verify that by finding the record in the REPL console. The type field is nil. I can make it work like I want by adding the following line.

@log.maintenance.type = params[:maintenance][:type]

But that's ugly. What I'm wondering is why doesn't the create method set the type field as it does the name field just find?

The two types you see look like this in my schema.rb

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!


The specific answer is that the type attribute, like many of Rails's special attributes, is protected from mass assignment. (Look up :attr_protected in the documentation.)

The answer to the more general problem is that you're not trusting your models enough. If you want to create a record of type OilChange, you shouldn't call Maintenance.new or Maintenance.create. Call OilChange.new or OilChange.create instead, and Rails will automatically take care of setting the type and doing all the background work for you.


Try 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 want to have only subclasses of Maintenance in your database, do the following in your Maintenance class:

class Maintenance < ActiveRecord::Base
  validates_presence_of :type
  # other stuff goes here
end

This code will create an invalid Maintenance class when the params contain the wrong type. If they contain the right type, an instance of the specified class will be created.

If your models use namespaces you might look into this article instead of using Module.const_get:

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜