Rails: создание ассоциации has_one

100

Привет (огромный новичок в Rails), у меня есть следующие модели:

class Shop < ActiveRecord::Base
  belongs_to :user
  validates_uniqueness_of :title, :user_id, :message => "is already being used"
end

и

class User < ActiveRecord::Base
  has_one :shop, :dependent => :destroy
end

Когда я собираюсь создать новый магазин, я получаю следующую ошибку:

private method `create' called for nil:NilClass

Это мой контроллер:

@user = current_user
@shop = @user.shop.create(params[:shop])

Я пробовал разные варианты, читая руководства и учебные пособия здесь и там, но я больше запутался, чем раньше, и не могу заставить его работать. Любая помощь будет принята с благодарностью.

Неко
источник
Отредактирован заголовок вопроса, чтобы отразить вопрос. Дубликат использования сборки с ассоциацией has_one в рельсах
Марк-Андре Лафортюн,
1
вы также можете использовать@user.build_shop(params)
ImranNaqvi

Ответы:

123

Прежде всего, вот как делать то, что вы хотите:

@user = current_user
@shop = Shop.create(params[:shop])
@user.shop = @shop

Вот почему ваша версия не сработала:

Вы, наверное, думали, что это может сработать, потому что, если бы Пользователь имел has_manyотношение к Магазину, @user.shops.create(params[:shop]) это сработало бы . Однако между has_manyотношениями и has_oneотношениями есть большая разница :

С has_manyотношением shopsвозвращает объект коллекции ActiveRecord, у которого есть методы, которые вы можете использовать для добавления и удаления магазинов для пользователя. Один из таких методов - createсоздать новый магазин и добавить его пользователю.

С помощью has_oneотношения вы не получаете обратно такой объект коллекции, а просто объект Shop, принадлежащий пользователю - или ноль, если у пользователя еще нет магазина. Поскольку ни объекты Shop, ни nil не имеют createметода, вы не можете использовать createэтот способ с has_oneотношениями.

sepp2k
источник
Спасибо за ответ, sepp2k. Теперь я понимаю, почему мой код не работал.
Neko
119
Вы также можете использовать @user.create_shop(params[:shop]). См. Методы, добавленные has_one .
nates 08
Выбранный ответ работает, но решение @nates также работает. +1 вам обоим.
nfriend21 02
+1 к ответу, потому что мне было интересно то же самое, +1 к ответу, чтобы объяснить, почему это так, и +1 к комментарию, чтобы дать лучшее решение.
deivid
224

Более краткий способ сделать это:

@user.create_shop(params[:shop])

См. Методы, добавленные has_one, в руководствах по Ruby on Rails.

натс
источник
6
Это определенно более лучший подход
Magnum
7
Помните, что если вы создадите магазин несколько раз, он удалит предыдущий магазин. Например, если вы запустите, @user.create_shop(params[:shop_one_info])он создаст shop_one, НО, если вы запустите @user.create_shop(params[:shop_two_info]), он удалит первый магазин и создаст второй.
ecoding5
Приведенный выше комментарий об удалении предыдущего магазина относится к Rails 3.2.18, не знаю о более поздних версиях. Невозможно отредактировать комментарий через 5 минут -_-
ecoding5
Нашел решение, я не устанавливал уникальность для связанной модели, поэтому убедитесь, что вы делаете то, как оно настроено в модели Shop этого примера.
ecoding5
Вы также можете использовать@user.build_shop(params)
ImranNaqvi
7

Еще два способа, если вы хотите saveвместо create:

shop = @user.build_shop
shop.save

shop = Show.new
shop.user = @user
shop.save
Товарищ незнакомец
источник
1

Просто чтобы добавить к приведенным выше ответам -

@user.create_shop(params[:shop])

Приведенный выше синтаксис создает новую запись, но впоследствии удаляет аналогичную существующую запись.

В качестве альтернативы, если вы не хотите запускать обратный вызов удаления

Shop.create(user_id: user.id, title: 'Some unique title')

Эта ветка может быть полезна. кликните сюда

Rais
источник