Страницы

Поиск по вопросам

вторник, 24 декабря 2019 г.

Ruby on Rails Activerecord - id в модели перепутаны

#ruby_on_rails


Дело обстоит в 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 в записи перепутаны местами, но получение ассоциированных записей при этом
работает верно?
    


Ответы

Ответ 1



Потому что вы перепутали 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 у этого объекта возвращать эту запись.

Комментариев нет:

Отправить комментарий