2011-01-06 11 views
26

Tengo una declaración de creación para algunos modelos, pero está creando un registro dentro de una tabla de combinación independientemente de si el registro ya existe.En Rails, ¿cuál es la mejor manera de actualizar un registro o crear uno nuevo si no existe?

Aquí es lo que mi código es el siguiente:

@user = User.find(current_user) 
@event = Event.find(params[:id]) 
for interest in @event.interests 
@user.choices.create(:interest => interest, :score => 4) 
end 

El problema es que crea los registros no importa qué. Me gustaría crear un registro solo si ya no existe ningún registro; si existe un registro, me gustaría tomar el atributo del registro encontrado y sumar o restar 1.

He estado buscando algo que haya llamado find_or_create_by. ¿Qué hace esto cuando encuentra un registro? Me gustaría que tome el atributo actual :score y agregue 1.

¿Es posible encontrar o crear por id? No estoy seguro de qué atributo encontraría, ya que el modelo que estoy buscando es un modelo de combinación que solo tiene id claves externas y el atributo de puntuación.

me trataron

@user.choices.find_or_create_by_user(:user => @user.id, :interest => interest, :score => 4) 

pero tiene

método no definido find_by_user

¿Qué debo hacer?

+1

Es current_user ya una instancia del modelo de usuario? Si es así, no necesita volver a encontrarlo y solo puede usar 'current_user' en lugar de' @user = ... '. –

Respuesta

23

Suponiendo que el modelo Choice tiene una user_id (asociar a un usuario) y un interest_id (asociar con un interés), algo como esto debe hacer el truco:

@user = User.find(current_user) 
@event = Event.find(params[:id]) 

@event.interests.each do |interest| 
    choice = @user.choices.find_or_initialize_by_interest_id(interest.id) do |c| 
    c.score = 0 # Or whatever you want the initial value to be - 1 
    end 

    choice.score += 1 
    choice.save! 
end 

Algunas notas:

  1. No necesita incluir la columna user_id en el find_or_*_by_*, ya que ya ha indicado a Rails que solo obtenga choices que pertenece al @user.
  2. Estoy usando find_or_initialize_by_*, que es esencialmente lo mismo que find_or_create_by_*, con la única diferencia de que initialize no crea realmente el registro. Esto sería similar a Model.new en comparación con Model.create.
  3. El bloque que establece c.score = 0 solo se ejecuta si el registro no contiene no.
  4. choice.score += 1 actualizará el valor de puntuación para el registro, independientemente de si existe o no. Por lo tanto, la puntuación predeterminada c.score = 0 debe ser el valor inicial menos uno.
  5. Finalmente, choice.save! actualizará el registro (si ya existió) o creará el registro iniciado (si no lo hizo).
+0

vincular esta pregunta similar http://stackoverflow.com/questions/18747062/rails-create-or-update-magic – fatman13

+0

Esta respuesta debe ser actualizado, algunos métodos no son aptos para los carriles 4 –

7

find_or_create_by_user_id suena mejor

+0

¿Cuál es la diferencia? – MartinElvar

+1

esto no actualiza el registro? Lo encuentra, o lo crea. – baash05

59
my_class = ClassName.find_or_initialize_by_name(name) 

my_class.update_attributes({ 
    :street_address => self.street_address, 
    :city_name => self.city_name, 
    :zip_code => self.zip_code 
}) 
+0

¿Por qué votar? –

+6

no estoy seguro de por qué esto fue downvoted. esto fue útil para mí, ¡gracias por la contribución! – shedd

+0

Lo encontré útil también. Supongo que la votación negativa se debe a que solo abordaba parcialmente lo que el OP estaba pidiendo. –

0

Si está utilizando los carriles 4 No creo que crea los métodos de búsqueda como antes, por lo find_or_create_by_user no se crea automáticamente. En su lugar lo haría así:

@user = User.find(current_user) 
@event = Event.find(params[:id]) 
for interest in @event.interests 
@user.choices.find_or_create_by(:interest => interest) do |c| 
    c.score ||= 0 
    c.score += 1 
end 
end 
3

Además, en Rails 3 que puede hacer:

@user.choices.where(:user => @user.id, :interest => interest, :score => 4).first_or_create 
+0

'first_or_create' se prefiere en los carriles 4 , mientras tanto 'find_or_intialize_by_xxx (attribute)' está en desuso en favor de 'find_or_intialize_by (xxx: attribute)' –

0

En Rails 4 Puede utilizar find_or_create_by para obtener un objeto (si no existe, creará), luego use update para guardar o actualizar el registro, el método de actualización persistirá si no existe, de lo contrario actualizará el registro.

Por ejemplo

@edu = current_user.member_edu_basics.find_or_create_by(params.require(:member).permit(:school)) 

if @edu.update(params.require(:member).permit(:school, :majoy, :started, :ended)) 
Cuestiones relacionadas