Выбор моделей Rails через виртуальные атрибуты

У меня есть две модели рельсов Section & SectionRevision, Раздел - это в основном просто контейнер, который содержит все ревизии, относящиеся к самому себе. Так что большинство атрибутов для 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

SectionRevision:

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

И модели:

SectionRevision:

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_section,current_revision & position,

Проблема в том, что сейчас я хотел бы создать виртуальный атрибут, children который выбирает все разделы, где виртуальный атрибут parent_section.id равно self.id, Это вообще возможно? Я знаю, что приведенный выше код не будет работать, так как он выполняет запрос к несуществующему столбцу - и я не уверен, что доступ к экземплярам модели из модели "Разделы" не работает.

Может ли выполнить выборку на основе виртуальных атрибутов?


Я обновил модель на основе ответа 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 возвращать => [] как 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?

2 ответа

Я думаю, что пользовательские отношения хорошо подходят для этих случаев:

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 , так как бесполезен после последних правок

Похоже, вы должны улучшить свою модель, как заявили 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

Другие вопросы по тегам