Rails 4 - выбросы has_one и own_to

Я прочитал это, поэтому я понимаю разница.

Но я унаследовал приложение, которое ведет себя странно (я думаю, возможно, я ошибаюсь, и это нормально).

Есть 2 модели:

class Pod < ActiveRecord::Base  
  has_one :pod_admin
end

class PodAdmin < ActiveRecord::Base  
  belongs_to :pod
end

В консоли рельсов я пробовал это:

p = Pod.find(5)

и он показывает, что этот Pod имеет pod_admin_id значение 14. Это правильно.

Пытался сменить PodAdmin:

p.pod_admin = PodAdmin.last

и это вызывает эту ошибку:

NoMethodError: undefined method pod_admin_id for #<PodAdmin:0x007fa401f1e710>

Это почему? Что мне не хватает?

ИЗМЕНИТЬ

Основываясь на комментариях / ответах, не меняя моделей, я попробовал следующее:

pa = PodAdmin.last
pa.pod = p

и это работает, я вижу, что консоль возвращает последний PodAdmin с новым pod_id.

НО

pa.save

И

p.save

оба выдают ту же ошибку, что и раньше.

Если я посмотрю на схему базы данных, в таблице Pod есть поле pod_admin_id, а в таблице PodAdmin есть поле pod_id.

Я унаследовал эту схему, и мне просто интересно, правильно ли ее настроил исходный разработчик. Конечно, я должен иметь возможность обновлять отношения с любого направления - разве не в этом смысл создания has_one и own_to, чтобы у вас могли быть такие двунаправленные отношения?

ИЗМЕНИТЬ 2

Я обнаружил проблему, заключающуюся в том, что я добавил эту строку в таблицу PodAdmin вместо таблицы Pod:

  validates :pod_admin_id, uniqueness: {scope: :id, message: 'The Pod already has a PodAdmin'}

Приношу свои извинения, но, как вы видите, я пытаюсь не допустить, чтобы у Pod было 2 PodAdmins. Это подтверждает, что этого не достигается.

Я могу сделать это:

p = Pod.find(5)
pa_last = PodAdmin.last
pa_first = PodAdmin.first
pa_last = p
pa_first = p
pa_last.save
pa_first.save

и теперь оба pa имеют одинаковый pod_id. Как я могу этого избежать?

ИЗМЕНИТЬ 3

После долгого чтения и тестирования и благодарности @Anand и @Spickerman проблема заключалась в том, что предыдущий разработчик поместил внешний ключ в обе таблицы (has_one и belongs_to). Только таблица own_to должна иметь внешний ключ. Кроме того, отношения были неправильно определены. Однако исправление этого не гарантирует надежного решения. Я настоятельно рекомендую другим лицам с аналогичными проблемами прочитать это.


person rmcsharry    schedule 13.05.2016    source источник
comment
Ты делаешь это неправильно. С настройкой ассоциации pod_admins таблица должна иметь pod_id.   -  person Pavan    schedule 13.05.2016
comment
@Pavan прав, вам либо нужен pod_id в pod_admins таблице, либо Pod должен belongs_to :pod_admin, а PodAdmin has_one :pod   -  person Rob Di Marco    schedule 13.05.2016
comment
В таблице pod_admins ЕСТЬ поле pod_id. Я обновил свой вопрос, чтобы объяснить - кажется, я не могу обновить отношения ни с одной стороны.   -  person rmcsharry    schedule 14.05.2016


Ответы (2)


Вместо p.pod_admin = PodAdmin.last вызовите PodAdmin.last.pod = p - как уже упоминалось, pod_id находится в таблице PodAdmin, а не наоборот.

Обновлять:

Основываясь на обновлении вопроса, проблема заключается в том, что у вас есть ссылки на внешний ключ в обоих направлениях - у вас должен быть либо pod_id в таблице pod_admins, либо pod_admin_id в таблице pods, но не оба сразу. Удалите один из них с помощью новой миграции и повторите попытку.

> bundle exec rails g migration RemovePodIdFromPodAdmins

# db/migrations/XXXX_remove_pod_admin_id_from_pods
def change
  remove_column :pods, :pod_admin_id
end

связка exec rake db: миграция

Затем, как было предложено выше, позвоните

pa = PodAdmin.last
pa.pod = p
pa.save!
person Anand    schedule 13.05.2016
comment
Поскольку own_to находится в модели PodAdmin, не следует ли мне сохранить этот внешний ключ и лучше удалить foreign_key со стороны has_one (т.е. удалить PodAdminID из таблицы Pod)? - person rmcsharry; 14.05.2016
comment
Я удалил pod_admin_id из таблицы Pods. Я все еще могу дать 2 PodAdmins одного и того же Pod. Это безумие. - person rmcsharry; 14.05.2016
comment
Да, @rmcsharry, я обновляю ответ, чтобы указать на это. Если вы удалите pod_admin_id из таблицы подов, это все равно позволит двум pod_admins иметь один и тот же под, потому что «pod принадлежит_to pod_admin» означает, что каждый экземпляр может принадлежать какому-либо поду. Но поскольку у вас есть pod_admin has_one pod, pod.pod_admins будет неопределенным, тогда как pod.pod_admin будет определен (хотя какой из них он вернет, не определен). - person Anand; 14.05.2016
comment
Спасибо @anand за этот последний комментарий, и вы указали, что было 2 внешних ключа, поскольку это была фундаментальная ошибка, сделанная предыдущим разработчиком, которая в первую очередь вызывает эту проблему. Поэтому отмечу ваш как ответ. - person rmcsharry; 17.05.2016

Внешний ключ всегда принадлежит модели с ассоциацией belongs_to.

В вашем примере PodAdmin belongs_to Pod, поэтому ваша pod_admins таблица должна иметь столбец pod_id.

Или вы можете просто изменить свои модели на следующие, чтобы отразить схему вашей базы данных:

class Pod < ActiveRecord::Base  
  belongs_to :pod_admin
end

class PodAdmin < ActiveRecord::Base  
  has_one :pod
end
person spickermann    schedule 13.05.2016
comment
В таблице pod_admins есть столбец pod_id. Я обновил вопрос, добавив дополнительную информацию. - person rmcsharry; 14.05.2016
comment
Внешний ключ должен существовать только в таблице модели, у которой есть own_to. В другой таблице не должно быть такого столбца (и в этом столбце также нет необходимости). Чтобы два PodAdmin не имели один и тот же Pod, выберите мою вторую версию и переключите направление, если ассоциация own_to. - person spickermann; 14.05.2016
comment
Спасибо, я попробовал это, удалив столбец из таблицы has_one. Я все еще могу назначить более одного PodAdmin для Pod. Я попробую вашу вторую версию, но уверен, что тогда можно будет назначить более одного модуля для каждого PodAdmin, что не должно быть разрешено, поскольку это не отношение 1-n. Кажется, я должен применить это ограничение на уровне базы данных. - person rmcsharry; 14.05.2016
comment
Конечно, вам понадобится уникальный индекс в базе данных, чтобы гарантировать уникальность pod_admin_id. Возможно, вы захотите сначала проверить Rails с validates :pod_admin_id, uniqueness: true (без применения id!). Но это помогает не во всех крайних случаях. - person spickermann; 14.05.2016