I use Ruby in Rails 4.2.0 with Ruby 2.1.0 and I have a self join association. Here is my migration:
class CreateStateTemplates < ActiveRecord::Migration
def change
create_table :state_templates do |t|
t.string :name
t.references :next
t.references :prev
t.timestamps null: false
end
end
end
And here is my model:
class StateTemplate < ActiveRecord::Base
has_one :prev, :class_name => "StateTemplate", :foreign_key => "prev_id"
belongs_to :state_template, :class_name => "StateTemplate", :foreign_key => "prev_id"
has_one :next, :class_name => "StateTemplate", :foreign_key => "next_id"
belongs_to :state_template, :class_name => "StateTemplate", :foreign_key => "next_id"
end
Here I fill some states:
StateTemplate.find(1).update(:next => StateTemplate.find(2))
StateTemplate.find(2).update(:next => StateTemplate.find(3), :prev => StateTemplate.find(1))
StateTemplate.find(3).update(:next => StateTemplate.find(4), :prev => StateTemplate.find(2))
StateTemplate.find(4).update(:next => StateTemplate.find(5), :prev => StateTemplate.find(3))
StateTemplate.find(5).update(:prev => StateTemplate.find(4))
But something strange has been happened. When I take one record from StateTemplate and put it on a concole, I've got this:
2.1.0 :007 > StateTemplate.find(2)
StateTemplate Load (0.1ms) SELECT "state_templates".* FROM "state_templates" WHERE "state_templates"."id" = ? LIMIT 1 [["id", 2]]
=> #<StateTemplate id: 2, name: "two", next_id: 1, prev_id: 3, created_at: "2017-07-22 11:51:30", updated_at: "2017-07-22 11:51:30">
Which doesn't right, because next_id
must be 3, but when I refer to the next state, I've got this:
2.1.0 :008 > StateTemplate.find(2).next
StateTemplate Load (0.1ms) SELECT "state_templates".* FROM "state_templates" WHERE "state_templates"."id" = ? LIMIT 1 [["id", 2]]
StateTemplate Load (0.0ms) SELECT "state_templates".* FROM "state_templates" WHERE "state_templates"."next_id" = ? LIMIT 1 [["next_id", 2]]
=> #<StateTemplate id: 3, name: "three", next_id: 2, prev_id: 4, created_at: "2017-07-22 11:51:30", updated_at: "2017-07-22 11:51:30">
Which is right. And here is my question: why does ids in record is exchanged, but refered to a right record?
Both belongs_to
associations have the same name (:state_template
), but they need not have different names. Furthermore I think it is a bit confusing to have prev
and next
pointing to self
instead of pointing to the linked templates.
I would change the associations like this:
belongs_to :next, class_name: 'StateTemplate'
belongs_to :prev, class_name: 'StateTemplate'
has_one :predecessor, foreign_key: 'next_id', class_name: 'StateTemplate'
has_one :successor, foreign_key: 'prev_id', class_name: 'StateTemplate'
And this can even be simplified. Because if a template has a belongs_to :next
than the reverse has_one
is automatically the prev
. Therefore remove the prev_id
from that model and just use the following:
belongs_to :next, class_name: 'StateTemplate'
has_one :prev, foreign_key: 'next_id', class_name: 'StateTemplate'