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.


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.


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.


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

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í

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.