Rails: использование сборки с ассоциацией has_one в рельсах

144

В этом примере я создаю объект userбез profile, а затем создаю profileдля этого пользователя. Я пробовал использовать сборку с has_oneассоциацией, но это не удалось. Единственный способ, которым я вижу эту работу, - использовать has_many. userДолжен иметь не более одной profile.

Я пробовал это. У меня есть:

class User < ActiveRecord::Base
  has_one :profile
end

class Profile < ActiveRecord::Base
  belongs_to :user
end

Но когда я сделаю:

user.build_profile 

Я получаю сообщение об ошибке:

ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'profiles.user_id' in 'where clause': SELECT * FROM `profiles` WHERE (`profiles`.user_id = 4)  LIMIT 1

Есть ли способ в рельсах иметь 0 или 1 ассоциацию?

шпинет
источник
что именно ты пробовал? не могли бы вы опубликовать код?
Джу Ногейра,

Ответы:

361

buildМетод подпись различна для has_oneи has_manyассоциаций.

class User < ActiveRecord::Base
  has_one :profile
  has_many :messages
end

Синтаксис сборки для has_manyассоциации:

user.messages.build

Синтаксис сборки для has_oneассоциации:

user.build_profile  # this will work

user.profile.build  # this will throw error

Прочтите документациюhas_one ассоциации для более подробной информации.

Хариш Шетти
источник
29
Меня всегда зацепляет другой синтаксис has_one ... черт возьми!
Galaxy
12
Забавно, что самый популярный и принятый ответ здесь отвечает на вопрос, отличный от того, который задал ОП.
Ajedi32
Предположительно, если пользователь принадлежал к профилю (это означает, что таблица пользователей имеет foreign_key profile_id в своей таблице), тогда также создание профиля для пользователя будет работать, как указано выше, то есть для нового действия только user.build_profile для редактирования, user.build_profile if user.profile.nil? и если вы хотите создать профиль при создании пользователя, напишите accepts_nested_attributes_for :profileэто в Модель пользователя. и в форме, в которой создается пользователь, напишите <%= f.simple_fields_for :profile do |p| %>это и продолжайте.
рвение
но почему это различное поведение было сохранено для has_one или has_many? Думаю и ожидаю, что при проектировании будет какая-то причина.
любознательный
@ Ajedi32 ответ соответствует заголовку вопроса, но не тексту. Учитывая, что this ( build_<association>) - довольно странное и неожиданное поведение в Rails, гораздо больше людей ищут этот ответ, чем фактический ответ на вопросы, если вы понимаете, о чем я.
Макс Уильямс
19

Внимательно посмотрите на сообщение об ошибке. Это означает, что у вас нет обязательного столбца user_idв таблице профиля . Установление отношений в модели - это только часть ответа.

Вам также необходимо создать миграцию, которая добавляет user_idстолбец в таблицу профиля. Rails ожидает, что это будет, и если это не так, вы не сможете получить доступ к профилю.

Для получения дополнительной информации перейдите по этой ссылке:

Основы ассоциации

Сосборн
источник
1
Я только что понял свою проблему. Книга, из которой я учусь, не очень хорошо объясняла создание внешнего ключа. Я создал новую миграцию, которая добавляет к моей модели внешний ключ. Спасибо.
espinet
Вам нужно каждый раз создавать колонку самостоятельно? У меня была идея, что это произошло автоматически. Не знаю, откуда у меня эта идея.
Rimian
Вы можете добавить столбец при создании модели с помощью командной строки, например rails g model profile user:references:index address:string bio:text.
Дуйхоа,
1

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

old_profile = instance_method(:profile)
define_method(:profile) do
  old_profile.bind(self).call || build_profile
end

теперь вызов #profileметода либо вернет связанный профиль, либо создаст новый экземпляр.

источник: Можете ли вы вызвать переопределенный метод из новой реализации, когда обезьяна исправляет метод?

Шиясон
источник
1
в текущих рельсах (проверено на 6.0.2.2) , вы можете упростить это: def profile; super || build_profile; end.
glasz
-14

Это должен быть файл has_one. Если buildне работает, вы можете просто использовать new:

ModelName.new( :owner => @owner )

такой же как

@owner.model_names.build
Карл
источник
11
Это не одно и то же: если вы создаете новое имя_модели с помощью build, при сохранении @owner новое имя_модели также будет сохранено. Итак, вы можете использовать build, чтобы родитель и дети были сохранены вместе. Это не тот случай, если вы создаете имя_модели с .new
Макс Уильямс