2016-05-13 3 views
1

Я прочитал this, поэтому я понимаю разницу.Rails 4 - has_one и принадлежит_от ошибки метания

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

Есть 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> 

Почему это? Что мне не хватает?

EDIT

на основе комментариев/ответов, не меняя модели, я попытался это:

pa = PodAdmin.last 
pa.pod = p 

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

НО

pa.save 

И

p.save 

как бросить ту же ошибку, как и раньше.

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

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

EDIT 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 

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

EDIT 3

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

+2

Вы делаете это неправильно. При настройке вашей ассоциации таблица 'pod_admins' должна иметь' pod_id'. – Pavan

+0

@Pavan прав, вам нужно либо 'pod_id' в таблице' pod_admins', либо 'Pod' должно' принадлежит_to: pod_admin', а 'PodAdmin'' has_one: pod' –

+0

pod_admins table У них есть поле pod_id. Я обновил свой вопрос, чтобы объяснить - кажется, я не могу обновить отношения с обеих сторон. – rmcsharry

ответ

1

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

Update:

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

> bundle exec rails g migration RemovePodIdFromPodAdmins 

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

bundle exec rake db:migrate

Тогда, как было отмечено выше, вызовите

pa = PodAdmin.last 
pa.pod = p 
pa.save! 
+0

Поскольку имя принадлежит классу PodAdmin, не следует ли хранить этот внешний ключ и удалять foreign_key с стороны has_one (то есть удалить PodAdminID из таблицы Pod)? – rmcsharry

+0

Я удалил pod_admin_id из таблицы Pods. Я все еще могу дать 2 PodAdmins того же Pod. Это орехи. – rmcsharry

+0

Да, @rmcsharry - Я обновляю ответ, чтобы указать на это.Если вы удалите pod_admin_id из таблицы pods, он все равно разрешит двум pod_admins иметь один и тот же pod, потому что 'pod принадлежит_to pod_admin' означает, что каждый экземпляр может принадлежать некоторому pod. Но, поскольку у вас есть pod_admin has_one pod, pod.pod_admins будет неопределенным, тогда как pod.pod_admin будет определен (хотя тот, который он вернет, не определен). – Anand

1

Иностранный ключ всегда относится к Модели с ассоциацией belongs_to.

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

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

class Pod < ActiveRecord::Base 
    belongs_to :pod_admin 
end 

class PodAdmin < ActiveRecord::Base 
    has_one :pod 
end 
+0

Таблица pod_admins имеет столбец pod_id. Я уточнил вопрос с дополнительной информацией. – rmcsharry

+0

Внешний ключ должен существовать только в таблице модели, у которой есть принадлежность. В другой таблице не должно быть такого столбца (а также нет необходимости в этом столбце). Чтобы два PodAdmin не имели одного и того же Pod, выберите мою вторую версию и переключите направление, если ассоциация belongs_to. – spickermann

+0

Спасибо, я попробовал это, удалив столбец из таблицы «has_one». Я все еще могу назначить более PodAdmin для Pod. Я попробую вторую версию, но вполне уверен, что тогда будет возможно назначить более одного Pod каждому PodAdmin, что не должно быть разрешено, поскольку это не 1-n отношение. Кажется, мне нужно обеспечить это ограничение на уровне базы данных. – rmcsharry

Смежные вопросы