【问题标题】:Selecting Rails Models via Virtual Attributes通过虚拟属性选择 Rails 模型
【发布时间】:2013-04-04 07:35:19
【问题描述】:

我有两个导轨型号SectionSectionRevision。一个部分主要是一个容器,其中包含与其自身相关的所有修订。所以Section 的大部分属性基本上都存储在SectionRevision 模型中,因此可以随时恢复到修订历史。

有时我需要从截面模型访问最新版本的属性,所以我创建了一些虚拟属性来解决这个问题。


每个模型都具有这些迁移中定义的属性:

部分:

class CreateSections < ActiveRecord::Migration
  def change
    create_table :sections do |t|
      t.integer "page_id", :null => false

      t.timestamps
      t.datetime "deleted_at"
    end
    add_index("sections", "page_id")
    add_index("sections", "current_revision_id")
  end
end

章节修订:

class CreateSectionRevisions < ActiveRecord::Migration
  def change
    create_table :section_revisions do |t|
      t.integer "section_id", :null => false
      t.integer "parent_section_id"

      t.integer "position"
      t.string "title", :default => "", :null => false
      t.text "body", :null => false
      t.timestamps
    end
        add_index("section_revisions", "section_id")
        add_index("section_revisions", "parent_section_id")
  end
end

还有模特:

章节修订:

class SectionRevision < ActiveRecord::Base
  belongs_to :section, :class_name => 'Section', :foreign_key => 'section_id'
  belongs_to :parent_section, :class_name => 'Section', :foreign_key => 'parent_section_id'

  def parsed_json
    return JSON.parse(self.body)
  end
end

部分:

class Section < ActiveRecord::Base
  belongs_to :page
  has_many :revisions, :class_name => 'SectionRevision', :foreign_key => 'section_id'
  has_many :references

  def current_revision
    self.revisions.order('created_at DESC').first
  end

  def position
    self.current_revision.position
  end

  def parent_section
    self.current_revision.parent_section
  end

  def children
    Sections.where(:parent_section => self.id)
  end
end

如您所见,Section 有几个虚拟属性,例如 parent_sectioncurrent_revisionposition

现在的问题是我想创建一个虚拟属性children,它选择虚拟属性parent_section.id 等于self.id 的所有部分。这可能吗?我知道上面的代码不起作用,因为它对不存在的列进行查询 - 而且我不确定如何从模型“Sections”中访问 Model 实例似乎不起作用。

可以根据虚拟属性进行选择吗?


我已根据 ProGNOMmers 的回答更新了模型并获得以下信息:

class Section < ActiveRecord::Base
  has_many :revisions, :class_name => 'SectionRevision', 
                       :foreign_key => 'section_id'
 #Need to somehow modify :child_revisions to only be selected if it is the section_id's current_revision?
  has_many :child_revisions, :class_name => 'SectionRevision', 
                             :foreign_key => 'parent_section_id'
  has_many :children, :through => :child_revisions, 
                      :source => :section
end

情况1:这工作得很好。

1.9.3p392 :040 > section
 => #<Section id: 3, page_id: 10, created_at: "2013-04-02 01:31:42", updated_at: "2013-04-02 01:31:42", deleted_at: nil> 
1.9.3p392 :041 > sub_section
 => #<Section id: 4, page_id: 10, created_at: "2013-04-04 10:19:33", updated_at: "2013-04-04 10:19:33", deleted_at: nil> 
1.9.3p392 :042 > revision1
 => #<SectionRevision id: 5, section_id: 4, title: "test", body: "[{\"type\":\"testbody\"}]", created_at: "2013-04-04 10:21:46", updated_at: "2013-04-04 21:55:10", position: 3, parent_section_id: nil> 
1.9.3p392 :043 > revision2
 => #<SectionRevision id: 6, section_id: 4, title: "test", body: "[{\"type\":\"testbody\"}]", created_at: "2013-04-04 12:29:19", updated_at: "2013-04-04 21:55:15", position: 3, parent_section_id: 3> 
1.9.3p392 :044 > sub_section.current_revision
  SectionRevision Load (0.6ms)  SELECT `section_revisions`.* FROM `section_revisions` WHERE `section_revisions`.`section_id` = 4 ORDER BY created_at DESC LIMIT 1
 => #<SectionRevision id: 6, section_id: 4, title: "test", body: "[{\"type\":\"testbody\"}]", created_at: "2013-04-04 12:29:19", updated_at: "2013-04-04 21:55:15", position: 3, parent_section_id: 3> 
1.9.3p392 :045 > section.children
 => [#<Section id: 4, page_id: 10, created_at: "2013-04-04 10:19:33", updated_at: "2013-04-04 10:19:33", deleted_at: nil>] 

情况2:

1.9.3p392 :021 > section
 => #<Section id: 3, page_id: 10, created_at: "2013-04-02 01:31:42", updated_at: "2013-04-02 01:31:42", deleted_at: nil> 
1.9.3p392 :022 > sub_section
 => #<Section id: 4, page_id: 10, created_at: "2013-04-04 10:19:33", updated_at: "2013-04-04 10:19:33", deleted_at: nil> 
1.9.3p392 :023 > revision1
 => #<SectionRevision id: 5, section_id: 4, title: "test", body: "[{\"type\":\"testbody\"}]", created_at: "2013-04-04 10:21:46", updated_at: "2013-04-04 10:24:22", position: 3, parent_section_id: 3> 
1.9.3p392 :024 > revision2
 => #<SectionRevision id: 6, section_id: 4, title: "test", body: "[{\"type\":\"testbody\"}]", created_at: "2013-04-04 12:29:19", updated_at: "2013-04-04 12:29:19", position: 3, parent_section_id: nil> 
1.9.3p392 :025 > sub_section.current_revision
  SectionRevision Load (0.7ms)  SELECT `section_revisions`.* FROM `section_revisions` WHERE `section_revisions`.`section_id` = 4 ORDER BY created_at DESC LIMIT 1
 => #<SectionRevision id: 6, section_id: 4, title: "test", body: "[{\"type\":\"testbody\"}]", created_at: "2013-04-04 12:29:19", updated_at: "2013-04-04 12:29:19", position: 3, parent_section_id: nil> 
1.9.3p392 :026 > section.children
  Section Load (0.6ms)  SELECT `sections`.* FROM `sections` INNER JOIN `section_revisions` ON `sections`.`id` = `section_revisions`.`section_id` WHERE `section_revisions`.`parent_section_id` = 3
 => [#<Section id: 4, page_id: 10, created_at: "2013-04-04 10:19:33", updated_at: "2013-04-04 10:19:33", deleted_at: nil>] 

在情况 2 中,我希望 section.children=&gt; [] 返回为 sub_section.current_revision.parent_section_id = nil 而不是 section.id

换句话说,section.children 应该返回所有Sections,其中.current_revision.parent_section_id = section.id 但我无法查询,因为.current_revision 是一个虚拟属性。

是否有可能将Section.current_revision 变成某种关联?或者也许唯一的方法是将current_revision 列添加到sections table

【问题讨论】:

    标签: ruby-on-rails ruby model attributes virtual-attribute


    【解决方案1】:

    我认为自定义关系非常适合这种情况:

    class Section < ActiveRecord::Base
      has_many :revisions, :class_name => 'SectionRevision', 
                           :foreign_key => 'section_id'
      has_many :child_revisions, :class_name => 'SectionRevision', 
                                 :foreign_key => 'parent_section_id'
      has_many :children, :through => :child_revisions, 
                          :source => :section
    end
    
    Section.find(42).children 
    #=> SELECT ... WHERE ... AND section_revisions.parent_section = 42
    

    代码我没试过,可能有错误,但思路应该是对的。

    我删除了关于:conditions的部分,因为上次编辑后没有用

    【讨论】:

    • 您能解释一下:conditions =&gt; proc 的作用吗?
    • 我不得不将其更改为:conditions =&gt; proc{ { :parent_section_id =&gt; id } },但我得到一个空数组作为回报。我已经用详细信息更新了上面的问题。
    • 我用更多信息更新了答案。空数组应该意味着没有符合条件的修订;检查children.to_sql,以验证生成的SQL是否正确
    • 查看你的 irb 输出 section_id 不应该匹配(s.section_id 是 3,sr.section_id 是 4);也许关系是错误的:试试has_many :children, :class_name =&gt; 'SectionRevision', :foreign_key =&gt; 'parent_section_id'(没有:conditions
    • 是的,类似于has_many :child_revisions, :class_name =&gt; 'SectionRevision', :foreign_key =&gt; 'parent_section_id'has_many :children, :through =&gt; :child_revisions, :source =&gt; :section
    【解决方案2】:

    看起来你应该像 ProGNOMmers 所说的那样改进你的模型;您可以使用以下一些宝石:

    但从字面上回答您的问题,您可以尝试将“children”方法添加到您的 SectionRevision 模型并将 Section#children 委托给 current_revision。

    class SectionRevision
      def children
        SectionRevision.where(:parent_section => self.id) # parent_section IS a column of SectionRevision
      end
    end
    
    class Section
      def children
        current_revision.children
      end
    end
    

    顺便说一句,您可以使用#delegate 进行委派:

    class Section
      delegate :children, :position, :parent_section, to: :current_revision
      def current_revision
        Section.where(:parent_section => self.id)
      end
    end
    

    http://apidock.com/rails/Module/delegate

    【讨论】:

    • 谢谢,这不太适用,因为:parent_sectionSection 中的虚拟属性,并且不作为可查询的列存在。尽管我不完全理解 :conditions =&gt; proc 的工作原理,但我会尝试改进上述模型。
    • 我犯了一个错误 - 请在 SectionRevision#children 中查看我的编辑
    猜你喜欢
    • 1970-01-01
    • 2011-03-11
    • 2023-03-17
    • 1970-01-01
    • 2014-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多