Rails: uso de build con una asociación has_one en rails

143

En este ejemplo, creo un usersin profile, luego creo un profilepara ese usuario. Intenté usar build con una has_oneasociación, pero eso explotó. La única forma en que veo que esto funciona es usando has_many. Se usersupone que el solo tiene como máximo uno profile.

He estado intentando esto Yo tengo:

class User < ActiveRecord::Base
  has_one :profile
end

class Profile < ActiveRecord::Base
  belongs_to :user
end

Pero cuando lo hago:

user.build_profile 

Me sale el error:

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

¿Hay alguna forma en los rieles para tener 0 o 1 asociación?

espinet
fuente
¿Qué intentaste exactamente? ¿podría, por favor, publicar algún código?
Ju Nogueira

Respuestas:

359

La buildfirma del método es diferente para has_oney las has_manyasociaciones.

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

La sintaxis de compilación para la has_manyasociación:

user.messages.build

La sintaxis de compilación para la has_oneasociación:

user.build_profile  # this will work

user.profile.build  # this will throw error

Lea la documentación de la has_oneasociación para más detalles.

Harish Shetty
fuente
28
La diferente sintaxis para has_one siempre me atrapa ... ¡carajo!
Galaxy
11
Es curioso cómo la respuesta mejor calificada y aceptada aquí está respondiendo una pregunta diferente de la que hizo el OP.
Ajedi32
Supuestamente, si el usuario pertenecía al perfil (lo que significa que la tabla de usuario tiene una clave_perfil_extranjera id_perfil en su tabla), también crear un perfil para el usuario funcionará como se mencionó anteriormente, es decir, pero para una nueva acción solo user.build_profile para editar user.build_profile if user.profile.nil? y si desea crear un perfil mientras crea un usuario, escriba accepts_nested_attributes_for :profileesto en Modelo de usuario. y en forma de qué usuario se está creando, escriba <%= f.simple_fields_for :profile do |p| %>esto y continúe.
celo
pero ¿por qué este comportamiento diferente se mantuvo para has_one o has_many? Habría alguna razón al diseñar, creo y espero.
curioso el
@ Ajedi32 la respuesta coincide con el título de la pregunta pero no con el cuerpo. Dado que este ( build_<association>) es un comportamiento bastante extraño e inesperado en Rails, hay muchas más personas que buscan esta respuesta que la respuesta de las preguntas reales, si sabes a lo que me refiero.
Max Williams
19

Mire bien el mensaje de error. Le dice que no tiene la columna requerida user_iden la tabla de perfil . Establecer las relaciones en el modelo es solo una parte de la respuesta.

También debe crear una migración que agregue la user_idcolumna a la tabla de perfil. Rails espera que esto esté allí y, si no es así, no puede acceder al perfil.

Para obtener más información, consulte este enlace:

Fundamentos de la asociación

sosborn
fuente
1
Acabo de resolver mi problema. El libro del que estoy aprendiendo no explica muy bien la creación de claves foráneas. Creé una nueva migración que agrega una clave externa a mi modelo. Gracias.
espinet
¿Necesita crear la columna usted mismo cada vez? Tenía la idea de que sucedió de forma automática. No sé de dónde saqué esa idea.
Rimian
Puede agregar la columna al generar un modelo usando la línea de comando, algo así rails g model profile user:references:index address:string bio:text.
Duykhoa
1

Dependiendo del caso de uso, puede ser conveniente ajustar el método y construir automáticamente la asociación cuando no se encuentra.

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

ahora llamando al #profilemétodo devolverá el perfil asociado o creará una nueva instancia.

fuente: cuando el mono parchea un método, ¿puede llamar al método anulado desde la nueva implementación?

Shiyason
fuente
1
en los carriles actuales (probado en 6.0.2.2) se puede simplificar esto a: def profile; super || build_profile; end.
glasz
-14

Debería ser a has_one. Si buildno funciona, puede usar new:

ModelName.new( :owner => @owner )

es lo mismo que

@owner.model_names.build
Karl
fuente
11
Esto no es lo mismo: si crea un nuevo model_name con build, cuando se guarda @owner, el nuevo model_name también se guardará. Por lo tanto, puede usar build para crear un padre y sus hijos que se guardarán juntos. Este no es el caso si crea un nombre_modelo con .new
Max Williams