Дело обстоит в Ruby on Rails 4.2.0 на Ruby 2.1.0.
Есть следующая миграция:
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
...такая модель:
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
Если выполнить следующее:
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))
Происходит нечто странное. Получение одного StateTemplate в консоли показывает следующее:
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]]
=> #
Что неверно, next_id должен быть 3. При этом метод next показывает следующее:
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]]
=> #
А это уже верно.
Почему id в записи перепутаны местами, но получение ассоциированных записей при этом работает верно?
Ответ
Потому что вы перепутали has_one и belongs_to. Для обеих сторон.
К тому же, сейчас у вас дважды определена ассоциация state_template, работать будет только последнее из этих определений. Уже это указывает, что что-то с определениями не так.
Посмотрим на next
Проблема тут:
has_one :next, :class_name => "StateTemplate", :foreign_key => "next_id"
belongs_to :state_template, :class_name => "StateTemplate", :foreign_key => "next_id"
Это должно быть:
belongs_to :next, :class_name => "StateTemplate"
И симметрично для prev
Что происходит?
Вы, наверное, думаете, что эти две строчки ведут себя примерно одинаково:
StateTemplate.find(1).update(:next => StateTemplate.find(2))
StateTemplate.find(1).update(:next_id => 2)
Но из ваших ассоциаций следует, что первая строчка изменяет второй объект (который в аргументе), ставя ему next_id = 1. Несмотря на то, что обновляете вы, казалось бы, первый.
Вы неправильно определили ассоциацию next
Сейчас ассоциированный — объект, у которого next_id равен id владельца. Ибо has_one
А хотели вы наоборот: получить объект, id которого равен next_id владельца. Это belongs_to
Запись, которая хранит в себе ключ ассоциированного объекта, относится к нему через belongs_to. А вот has_one и has_many хранят ключ не "в себе", а в ассоциированных объектах.
"Минус на минус даёт плюс". Апдейты сделали совсем не то, что вы подумали. И поиск ассоциированных записей работает не так, как вы хотите. Но оба пользуются одним определением и в сумме они работают верно: запись определённой записи в next действительно заставляет метод next у этого объекта возвращать эту запись.
Комментариев нет:
Отправить комментарий