【问题标题】:ActiveRecord Subclassing dilemmaActiveRecord 子类化困境
【发布时间】:2012-08-21 15:44:07
【问题描述】:

所以我们开始吧。我有一个 Activerecord::Base 模型,让它被称为人类。

class human < ActiveRecord::Base
   has_one :Animal
end

动物是一个抽象类——

class animal < ActiveRecord::Base
   self.abstract_class = true;
 end

我有一个动物的子类,让它成为狗

class dog < Animal

如果我不使用抽象类,我不能将实例变量添加到“狗”(因为它存储在“动物”表中)。如果我使用抽象类,我无法将“动物”添加到“人类” - 因为 rails 不知道如何存储,例如“狗”(ActiveRecord 错误:找不到表“”) .这种情况让我发疯,我无法克服它。 我是遗漏了什么还是完全做错了?

【问题讨论】:

  • 您的大小写错误。类名应该大写,关联符号不应该。

标签: ruby-on-rails ruby ruby-on-rails-3 activerecord subclassing


【解决方案1】:

按照 Ruby 中的约定,Animal 会引用一个类(实际上,它涉及的更多一些 - 这个链接有更多 detail)。在您的原始帖子中,“class dog”应该是“class Dog”b/c,类名是一个常量,如果您在人和动物之间有一个 has_one 关联,您可以说 human.animal = (some instance of animal) ,但是 human.Animal 如果不立即崩溃,可能会产生奇怪的效果。其他人推荐的 STI 方法将完全按照您的意愿进行,尽管您将设置“类型”值,而不是“动物”(请不要直接这样做)。

您应该阅读 Ruby 和 RoR、STI、活动记录关联和多态关联中大写的含义。像这样的东西应该可以工作(未经测试,而且标准化不好 - 您可以使用 has_one 关联和一种称为委托的模式来设置通用动物特征在一个表中,而“人类特定”特征在另一个表中以避免数据库中的一堆 NULL 列):

# remember to set up your migrations to add a 'type' column to your Animal table
# if animals can own other animals who own other animals, you may want to look at
# acts_as_tree, which does trees in relational databases efficiently 

class Animal < ActiveRecord::Base 
  self.abstract_class = true
end

class Dog < Animal
  # this is bad normalization - but you can keep this simple by adding 
  # a human_id field in your animal table (don't forget to index)
  # look into the 'belongs_to' / 'references' type available for DB migrations   
  belongs_to :human
end

class Human < Animal
  has_one :dog, :autosave => true # or you could use 'has_many :dogs' 
end

human = Human.new # => adds record to Animal table, with type = 'human'
dog = Dog.new
human.dog = dog
human.save

【讨论】:

    【解决方案2】:

    ActiveRecord 具有对多态关联的内置支持,因此您可以这样做:

    http://guides.rubyonrails.org/association_basics.html#polymorphic-associations

    【讨论】:

    • 这没有帮助,我这里没有多态关联。问题是 ActiveRecord 不能存储抽象类。
    • 如果你愿意,你可以拥有多态关联。制作一个名为 Dog 的表/模型和一个名为 Frog 的表/模型。然后将 module 称为 Animal 并将其包含在它们两者中。 (如果您尝试 Rich Drummond 的回答,我怀疑您也可以使用 Animal 基类来完成此操作,但模块可能更容易。)您需要迁移人类表以具有 animal_type 字符串字段。
    • 模块不是我的解决方案。所以现在我明白了,我在 Animal 类中拥有所有 Animal 子类的字段(并且不要使其抽象),并且方法存在于子类中。它应该不是那么酷,但因为我不知道更好的解决方案......
    【解决方案3】:

    ActiveRecord 默认从模型名称中获取表名。但是,您可以覆盖它。如果你想在一张桌子上放狗,在另一张桌子上放猫,等等,那么你可以这样做(在 Rails 3.2 中):

    class Dog < Animal
      self.table_name = 'dogs'
    end
    
    class Cat < Animal
      self.table_name = 'cats'
    end
    

    (您必须添加迁移才能创建这些表。)

    但是,如果您希望所有动物都存在于一张表中,则应查看 Single-Table-Inheritance。有关更多信息,请参阅ActiveRecord docs

    【讨论】:

    • 实际上并没有什么帮助。问题是 Dog.new 有效,但 dog = Dog.new human = Human.new human.Animal = dog - 没有!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多