在 Rails 中创建单表继承记录

发布于 2024-08-07 23:49:19 字数 1466 浏览 7 评论 0原文

我在我的应用程序中使用单表继承。我的多态类型是 Maintenance,目前只有一种子类型,名为 OilChange。我在控制器中的创建方法中创建记录时遇到问题。这是代码。

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

params[:maintenance] 哈希具有键 {:name, :type}。我可以通过打印出来来验证它们的存在和值,如下所示

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

如果我传入“OilChange”作为 :type 键的值,则维护记录的类型为 Maintenance,而不是 OilChange。我可以通过在 REPL 控制台中查找记录来验证这一点。类型字段为零。我可以通过添加以下行使其按我想要的方式工作。

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

但这很难看。我想知道为什么 create 方法不像 name 字段那样设置 type 字段?

您在我的 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"

我的模型看起来像这样。

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

蒂亚!

I'm using single table inheritance for my application. My polymorphic 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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

╄→承喏 2024-08-14 23:49:19

具体答案是,type 属性与 Rails 的许多特殊属性一样,受到保护,不会被批量赋值。 (在文档中查找:attr_protected。)

更普遍的问题的答案是您对模型的信任度不够。如果您想要创建 OilChange 类型的记录,则不应调用 Maintenance.newMaintenance.create。相反,调用 OilChange.newOilChange.create,Rails 将自动为您设置类型并完成所有后台工作。

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.

何其悲哀 2024-08-14 23:49:19

尝试以下代码:

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

如果您只想在数据库中包含 Maintenance 的子类,请在 Maintenance 类中执行以下操作:

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

当参数包含错误类型时,此代码将创建无效的 Maintenance 类。如果它们包含正确的类型,则将创建指定类的实例。

如果您的模型使用命名空间,您可以查看本文而不是使用 Module.const_get

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:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文