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?


¿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.


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


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?


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

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
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.