Rails: crear en la asociación has_one

100

Hola (gran novato de Rails aquí), tengo los siguientes modelos:

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

y

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

Cuando estoy a punto de crear una nueva tienda, aparece el siguiente error:

private method `create' called for nil:NilClass

Este es mi controlador:

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

He probado diferentes variaciones leyendo guías y tutoriales aquí y allá, pero estoy más confundido que antes y no puedo hacer que funcione. Cualquier ayuda será muy apreciada.

Neko
fuente
Título de la pregunta editada para reflejar la pregunta. Duplicado de Usar compilación con una asociación has_one en rieles
Marc-André Lafortune
1
también puede usar@user.build_shop(params)
ImranNaqvi

Respuestas:

123

En primer lugar, aquí se explica cómo hacer lo que quiere:

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

Ahora, he aquí por qué su versión no funcionó:

Probablemente pensó que este trabajo podría, porque si el usuario tenía una has_manyrelación con la tienda, @user.shops.create(params[:shop]) sería trabajar. Sin embargo, existe una gran diferencia entre has_manyrelaciones y has_onerelaciones:

Con una has_manyrelación, shopsdevuelve un objeto de colección ActiveRecord, que tiene métodos que puede usar para agregar y eliminar tiendas de un usuario. Uno de esos métodos es create, que crea una nueva tienda y la agrega al usuario.

Con una has_onerelación, no recupera tal objeto de colección, sino simplemente el objeto Tienda que pertenece al usuario, o nulo si el usuario aún no tiene una tienda. Dado que ni Shop objects ni nil tienen un createmétodo, no se puede usar de createesta manera con las has_onerelaciones.

sepp2k
fuente
Gracias por tu respuesta, sepp2k. Ahora veo por qué mi código no podía funcionar.
Neko
118
También puede utilizar @user.create_shop(params[:shop]). Vea los métodos agregados por has_one .
nates
La respuesta elegida funciona, pero la solución @nates también funciona. +1 para los dos.
nfriend21
+1 a la respuesta porque me preguntaba lo mismo, +1 a la respuesta para explicar por qué es así y +1 al comentario para dar la mejor solución.
deivid
224

Una forma más concisa de hacer esto es con:

@user.create_shop(params[:shop])

Vea los métodos agregados por has_one en las guías de Ruby on Rails.

nates
fuente
6
Este es definitivamente un enfoque mejor
Magnum
7
Tenga en cuenta que si crea_shop más de una vez, se eliminará la tienda anterior. Por ejemplo, si lo ejecuta @user.create_shop(params[:shop_one_info]), creará shop_one, PERO si lo ejecuta @user.create_shop(params[:shop_two_info]), eliminará la primera tienda y creará la segunda.
ecoding5
El comentario anterior sobre la eliminación de la tienda anterior es para Rails 3.2.18, no sé sobre versiones más recientes. No se puede editar el comentario después de 5 min -_-
ecoding5
Encontré una solución, no configuré la unicidad en el modelo asociado, así que asegúrese de hacerlo como está configurado en el modelo Shop de este ejemplo.
ecoding5
también puede usar@user.build_shop(params)
ImranNaqvi
7

Dos formas más si lo desea en savelugar de create:

shop = @user.build_shop
shop.save

shop = Show.new
shop.user = @user
shop.save
Compañero extraño
fuente
1

Solo para agregar a las respuestas anteriores:

@user.create_shop(params[:shop])

La sintaxis anterior crea un nuevo registro, pero posteriormente elimina un registro similar existente.

Alternativamente, si no desea activar eliminar devolución de llamada

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

Este hilo puede ser útil. haga clic aquí

Rais
fuente