我该如何建模这些关系?

时间:2010-12-04 13:46:09

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

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

class Post < ActiveRecord::Base
  has_many :snippets
end

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

我希望有4个不同类型的代码段,例如:

  • body(text)
  • image(string)
  • 代码(文字)
  • video(string)

Q1

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

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

我觉得在片段模型上有一个“类型”字段似乎不是一个好主意,因为这可能会导致数据库和代码的强耦合。在这种情况下,是否有某种铁轨技术可以帮助我?

3 个答案:

答案 0 :(得分:5)

实际上,我有点像type字段。你可以使用single table inheritance的魔力,但众所周知,它是一个脆弱的系统,无论如何,包括一个type字段。

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

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

就你在模板中所做的事情而言,你可以用耳朵来玩。一个干净的解决方案可能是一个简单的帮助,可以像snippet_content那样调用,基于type字段,它会调用助手snippet_content_textsnippet_content_imagesnippet_content_codesnippet_content_video。或者您可以在模板中执行if-then分支,或者引用任意数量的部分模板(但要小心,因为在不必要的情况下使用时会变慢)。

答案 1 :(得分:2)

  1. 我喜欢这种方法,但后来我总会遇到一些细微的并发症。

  2. 创建适当的视图(texts/_text.html.hamlimages/_image.html.haml等),以便让Rails为您处理:

    = render @post.snippets
    
  3. 喜欢与否:“type”-field是让Rails魔法帮助你创建适当实例的方法。

  4. 更新:多态关系也起作用,让每个片段都有自己的表。它是关系还是表现得像类型的关系?你可以说图像和文本都表现得像代码片段。在这种情况下,请转到名为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
    

答案 2 :(得分: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添加到帖子模型
  • 在代码段模型中创建一个名为“snippetable”的多态关联
  • 通过我的迁移文件
  • 向代码段表添加一些新字段

这是我的代码:

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 console并执行以下操作:

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 !!!

Yaldi!

无论如何,我花了11天的时间来解决这个问题,它真的让我感到沮丧,我无法做到。我希望这有助于某人。

以下是acts_as_polymorph的一些不错的阅读材料:

进行什么