如何对这些关系进行建模?

3

我有一个帖子模型。每个帖子都有一个标题和许多片段

class Post < ActiveRecord::Base
  has_many :snippets
end

class Snippet < ActiveRecord::Base
  belongs_to :post
  attr_accessible :position, :post_id
end

我希望有四种不同类型的代码片段,分别是:

  • 正文(文本)
  • 图片(字符串)
  • 代码(文本)
  • 视频(字符串)

问题1

我应该创建四个新模型(称为文本、代码、视频和图像),并像这样扩展片段模型吗?

class Text < Snipppet
  attr_accessible :body
end

class Image < Snippet
  attr_accessible :image
end

class Video < Snippet
  attr_accessible :title
end

class Code < Snippet
  attr_accessible code
end

Q2

当每个片段可能是4种不同的内容时,我该如何在视图中引用每个片段的内容?

在我的视图中,我想要这样做:

- for snippet in @post.snippets
  = place the content of the snippet here

Q3

我认为在片段模型上加入“类型”字段并不是一个好主意,因为这可能会导致数据库与代码之间产生强耦合。在这种情况下,是否有一些Rails技巧可以帮助我解决呢?

3个回答

5

我实际上很喜欢type字段。你可以使用单表继承的神奇功能,但众所周知这是一个脆弱的系统,而且仍然包括type字段。

然后你可以使用多态解决方案,但是似乎用四个不同的表来表示几乎完全相同的东西有点傻。

老实说,使用简单的type字段并根据它改变解释可能会给你最好的结果和最少的头疼。

至于在模板中该怎么做,你可以根据需要灵活处理。一个干净的解决方案可能是一个简单的帮助程序,像snippet_content,基于type字段,将调用snippet_content_textsnippet_content_imagesnippet_content_codesnippet_content_video等帮助程序。或者你可以在模板中进行if-then分支,或者引用任意数量的局部模板(但要小心使用,因为如果不必要地使用这些模板,它们会变得很慢)。


+1 对于单表继承的支持。但是,如果这4个代码片段所涉及的属性是不同的,那么单表继承就会变得混乱,因为所有这些属性都需要放在同一个表中。在这种情况下,多态性是更好的解决方案。 - Chirantan
@Chirantan:不是的,我只是在谈论解释单个文本字段的不同辅助方法。不过我应该事先澄清一下,现在正在编辑 :) - Matchu
非常感谢您的回答。我觉得很有帮助。我已经发布了自己的答案,详细说明了我是如何解决这个问题的。 - stephenmurdoch

2
  1. I like this approach, but I always run into some subtle complications later on.

  2. Create the appropriate views (texts/_text.html.haml, images/_image.html.haml, etc), so you can let Rails handle it for you:

    = render @post.snippets
    
  3. Like it or not: the "type"-field is the way to have Rails magic help you create the proper instances.

更新:多态关系也可以使用,让每个片段都有自己的表。这是一种“is a”关系还是“behaves like”类型的关系?你可以说Images和Texts都像snippets一样行事。在这种情况下,选择一个名为Snippet的模块并混合它进去。
class Post < ActiveRecord::Base
  has_many :snippets, :polymorphic => true
end

class Text < ActiveRecord::Base
  include Snippet
  belongs_to :post, :as => :snippet
end

module Snippet
  # shared behavior here
end

谢谢你的回答,我花了一些时间才弄清楚如何修复这个问题。我已经发布了自己的答案,解释了我是如何解决这个问题的。 - stephenmurdoch

2

非常感谢回答我的每一个人,但是我认为我已经成功通过使用jystewart的Rails 3版本的acts_as_polymorphs gem来修复了这个问题。

以下是我所做的:

让我们回忆一下,我们有帖子和四种不同类型的片段。那就是6个不同的模型(post.rb、snippet.rb、code.rb、text.rb、image.rb和video.rb)。

  • 帖子拥有多个片段。
  • 片段属于帖子。
  • 代码、文本、视频和图像都是片段的一种类型。

而且最大的问题是我们不知道片段是哪种类型的对象,因为它可以是4种不同的东西之一。

起初,我尝试使用单一表继承来解决这个问题,但是在我看来,这些对象(代码、文本、视频和图像)彼此之间的区别太大,无法很好地工作,而且我不喜欢它可能会导致许多空数据库单元格的事实,所以我选择了多态路线。

我在这里无法使用标准的多态关联,因为这不是您通常遇到的多态情况。通常,在处理多态性时,我们谈论的是像评论模型这样的东西,它可以附加到多个其他模型上。多态实体始终是相同的东西。但在这种情况下,我们正在讨论可以是4种不同事物之一的片段模型。这不是一个简单的belongs_to情况。多态性没有发生。

然后我偶然发现了m.onkey.org上的这篇文章 - 虽然已经过了几年,但他基本上解释了这种情况需要acts_as_polymorphs gem。

所以解决方案是执行以下操作:

  • 我创建了6个模型,所有模型都扩展了ActiveRecord :: Base
  • has_many_polymorphs添加到post模型中
  • 在snippet模型中创建了一个多态关联称为“snippetable”
  • 通过我的迁移文件向snippets表添加了一些新字段

这是我的代码:

class Post < ActiveRecord::Base    
  has_many_polymorphs :snippets, :from => [:codes, :texts, :videos, :images], :through => :snippets
end

class Snippet < ActiveRecord::Base
    belongs_to :snippetable, :polymorphic => true
end

class Code < ActiveRecord::Base
  # don't have to put anything in here  
end

class Text < ActiveRecord::Base
  # don't have to put anything in here          
end

class Video < ActiveRecord::Base
  # don't have to put anything in here  
end

class Image < ActiveRecord::Base
  # don't have to put anything in here  
end

我们需要的另一件事,就是在CreateSnippets迁移中添加一些新字段:

class CreateSnippets < ActiveRecord::Migration
  def self.up
    create_table :snippets do |t|
      t.references :post
      t.column :snippetable_id,   :integer, :null => false
      t.column :snippetable_type, :string,  :null => false

      t.timestamps
    end
  end
end

就是这样!难以置信,现在我可以进入Rails控制台并执行以下操作:

p = Post.first
p.codes << Code.create(:code=>"This is a code snippet")
p.images << Image.create(:image=>"xyz.png")
p.images << Image.create(:image=>"123.png")

p.codes.count # returns 1
p.images.count # returns 2
p.snippets.count # returns 3 !!!

雅尔蒂!

无论如何,花了我11天才解决这个问题,我真的很沮丧我无法解决它。我希望这能帮助到某些人。

以下是一些关于acts_as_polymorph的不错阅读资料:

真是漫长的过程


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接