ruby-on-rails 如何自定义Rails 3 STI列名

6yt4nkrj  于 12个月前  发布在  Ruby
关注(0)|答案(5)|浏览(161)

我有一个名为Post的类:

class Post < ActiveRecord::Base
end

字符串
我有一个名为Question的类,它继承自Post

class Question < Post
end


我有一个名为Answer的类,它也继承自'Post':

class Answer < Post
end


Post中,我有一个名为post_type_id的列,它的类型是Integer
如何使用STI和特定列名&类型从Post继承?0表示Question1表示Answer。(0 & 1是posts表中post_type_id的值)

xu3bshqb

xu3bshqb1#

你可以像这样改变单表继承列的名称:

class Post < ActiveRecord::Base
  self.inheritance_column = 'type_column_name'

end

字符串
然而,没有办法让Rails使用整数而不是将实际类型存储为字符串,这让我觉得这可能不是单目标继承的好用例。也许作用域更适合你:

class Post < ActiveRecord::Base
  scope :questions, where(:post_type_id => 0)
  scope :answers, where(:post_type_id => 1)

end

@questions = Post.questions.all
@answers = Post.answers.all

fivyi3re

fivyi3re2#

实际上有一种方法,就像所有的事情一样。你可以重写find_sti_class方法来基于整数查找你的类型。

eqoofvh9

eqoofvh93#

这可能就是你要找的
http://lorefnon.me/2014/07/27/optimizing-sti-columns.html
编辑:更新的URL(感谢@ Subhashmartran)

monwx1rj

monwx1rj4#

您需要使用self.inheritance_column设置列类型然后您必须为以下方法提供您自己的行为:“sti_class_for(type_name)”,该方法位于此文件“activerecord/lib/active_record/inheritance.rb”中
所以你将不得不对上面的文件进行修补。
这不是推荐的方法,您必须进行迁移并更改所有值以匹配您的类名。

hxzsmxv2

hxzsmxv25#

ENUM方式:

这不需要任何重写,因为传递给sti_class_for方法的值是枚举值,如果设置正确,则不需要任何进一步的干预。

class Post < ActiveRecord::Base
  self.inheritance_column = :post_type_id

  enum post_type_id: %w[Question Answer] # for simple implicit numbering based on order
    # or
  enum post_type_id: {"Question" => 10, "Answer" => 21 } # for explicit numbering
end

字符串

CONSTANT方法:

这需要重写,这在某些情况下不是优选的。

class Post < ActiveRecord::Base
  self.inheritance_column = :post_type_id

  POST_TYPE_ID_CLASSES = { 0 => "Craigslist", 1 => "ApartmentsDotCom" }.freeze

  self.sti_class_for(post_type_id)
    super(POST_TYPE_ID_CLASSES[post_type_id])
  end
end

其他案例:

我想补充一些澄清,这是从我发现这篇文章和当时的答案失踪。
self.inheritance_column是你需要的大部分内容。然而,根据该列数据的格式,你可能需要将其格式化为ActiveRecord所期望的格式,或者你可能会得到类似于以下错误的东西:

from /usr/local/bundle/gems/activerecord-7.0.7.2/lib/active_record/inheritance.rb:190:in `rescue in sti_class_for'
Caused by NameError: wrong constant name frequently_asked_questions

      Object.const_get(camel_cased_word)
            ^^^^^^^^^^


在我的例子中,数据是在snake_case而不是CamelCase中。sti_class_for方法的简单重写为我解决了这个问题。

class Post < ActiveRecord::Base
  self.inheritance_column = :my_column_name

  self.sti_class_for(type_name)
    super(type_name.camelize)
  end
end


这个问题也可以通过将数据以预期的格式存储在DB中来解决,然后在用于其他目的时转换为替代格式。您的需求可能非常,所以在决定走哪条路时请记住这一点。
注意事项:在我的例子中我没有选择enum的原因是为了避免使用和enum更改DB值的任何不可预见的副作用,因为虽然它是可能的并且有效,但它破坏了enum是a mapping for database integers to other values的意图。
Declare枚举属性,其中的值Map到数据库中的整数,但可以按名称查询
有一个blog post实际上提出了一个论点,要求对你的枚举进行严格的限制,这在documentation中提到,并对性能提出了警告:
最后,也可以使用字符串列来持久化枚举值。请注意,这可能会导致数据库查询速度变慢:

class Conversation < ActiveRecord::Base
  enum :status, active: "active", archived: "archived"
end


所以再一次,你的需求可能非常,所以相应地选择.

相关问题