Рейки: створити на об’єднанні 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])

Я спробував різні варіанти, читаючи тут і там посібники та навчальні посібники, але я більше розгублений, ніж раніше, і не можу змусити його працювати. Будь-яка допомога буде дуже вдячна.


Відредагований заголовок питання для відображення питання. Дублікат використання build з асоціацією has_one в рейках
Marc-André Lafortune

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, що належить користувачеві, - або нульовий, якщо у користувача ще немає магазину. Оскільки ані в магазинах, ані в магазинах, ані в nil немає createметоду, ви не можете використовувати createцей спосіб у has_oneвідносинах.


Дякую за вашу відповідь, sepp2k. Тепер я бачу, чому мій код не міг працювати.
Неко

118
Ви також можете використовувати @user.create_shop(params[:shop]). Дивіться методи, додані has_one .
nates

Обрана відповідь працює, але рішення @nates також працює. +1 для вас обох.
nfriend21

+1 до відповіді, тому що мені було цікаво те саме, +1 до відповіді, щоб пояснити, чому це так, і +1 до коментаря за найкраще рішення.
deivid

224

Більш стислий спосіб зробити це:

@user.create_shop(params[:shop])

Дивіться методи, які додав has_one в посібниках Ruby on Rails.


6
Це, безумовно, кращий підхід
Магнум

7
Будьте обережні, якщо ви створите_магазин більше одного разу, це видалить попередній магазин. Наприклад, якщо ви запустили, @user.create_shop(params[:shop_one_info])він створить shop_one, Але якщо ви запустите @user.create_shop(params[:shop_two_info]), він видалить перший магазин і створить другий.
ecoding5

Наведений вище коментар щодо видалення попереднього магазину стосується Rails 3.2.18, не знаю про новіші версії. Неможливо редагувати коментар через 5 хв -_-
ecoding5

Знайшовши рішення, я не встановив унікальність на пов’язаній моделі, тому переконайтеся, що ви зробите, як це налаштовано в цій моделі магазину цього прикладу.
ecoding5

ви можете використовувати @user.build_shop(params)
інше

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')

Ця тема може бути корисною. Натисніть тут

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.