Выбор моделей 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; Вы можете использовать некоторые из следующих драгоценных камней:
- родословная ( http://railscasts.com/episodes/262-trees-with-ancestry)
- awesome_nested_set ( https://github.com/collectiveidea/awesome_nested_set)
Но, отвечая буквально на ваш вопрос, вы можете попробовать добавить метод '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