I have the two rails models Section
& SectionRevision
. A Section is mostly just a container that holds all the Revisions relating to itself. So most of the attributes for the Section
are basically stored within the SectionRevision
model so there's a history of Revisions that can be reverted back to at any time.
Sometimes I need to access the attributes for the latest revision from the Sections Model so I've created some Virtual Attributes to account for this.
Each model has the attributes as defined in these migrations:
Section:
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
And the models:
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
Section:
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
As you can see Section
has a couple of virtual attributes like, parent_section
,current_revision
& position
.
The problem being now I would like to create a virtual attribute, children
that selects all sections where the virtual attribute parent_section.id
is equal to self.id
. Is this possible at all? I know the above code won't work as its doing a query for a column that doesn't exist - and I'm not sure how to access the Model instances from within the model 'Sections' doesn't appear to work.
Can a perform a selection based on virtual attributes?
I've updated the model based on ProGNOMmers answer and get the following:
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
Circumstance 1: This works perfectly fine.
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>]
Circumstance 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>]
In circumstance 2 I would like section.children
to return => []
as sub_section.current_revision.parent_section_id = nil
and not section.id
.
In other words section.children
should return all Sections
where .current_revision.parent_section_id = section.id
but I can't query that as .current_revision
is a virtual attribute.
Is it possible to maybe turn Section.current_revision
in to some sort of association? Or maybe the only way is to add a current_revision
column to the sections table
?