2011-02-12 10 views
5

Esto es lo que estoy tratando de lograr:¿Cómo comprobar si existe un registro antes de crear uno nuevo en rails3?

  • que tienen un sistema de etiquetado en su lugar.
  • etiquetas se crean, cuando se crean mensajes (mensajes has_many: etiquetas,: a través de =>: tag_joins
  • Una etiqueta de unión se crea automáticamente cuando se crea un post con las etiquetas.).

Quiero comprobar si la etiqueta ya existe. Si lo hace, quiero usar la etiqueta existente para el registro tag_join, en lugar de crear un nuevo registro de etiqueta.

Aquí está mi código actual, que no está funcionando.

class Tag < ActiveRecord :: Base 
    belongs_to :user 
    belongs_to :tag_join 
    belongs_to :post 

    before_create :check_exists 

    def check_exists 
    tag = Tag.where(:name => self.name, :user_id => current_user.id) 
    if tag.nil? 
     tag = Tag.create(:name => self.name, :user_id => current_user.id) 
    end 
    end 

end 

Esto no funciona sin embargo, estoy recibiendo un error en la creación de tareas ... (el servidor es en realidad el tiempo de espera - no recibo un error específico).

¿Alguna idea?

Tokland dijo que estaba creando un bucle infinito contando para crear la etiqueta de nuevo - por lo que intentó esto:

def check_exists 
     tag = Tag.find_by_name_and_user_id(:name => self.name, :user_id => current_user.id) 
     if tag != nil 
     self.id = tag.id 
     end 
    end 

y aún así obtener el tiempo de espera del servidor

Editar: No estoy seguro de si esto importa, pero la forma en que se agregan las etiquetas es similar a "http://railscasts.com/episodes/73-complex-forms-part-1

están anidados en el formulario de publicación y usan algo de esta manera:

def tag_attributes=(tag_attributes) 
    tag_attributes.each do |attributes| 
    tags.build(attributes) 
    end 
end 

Me pregunto si esto está impidiendo que todo funcione. Además, el uso current_user.id en el modelo sin duda parece ser un problema ...

EDIT:

Algo que he descubierto: esto tenía que cambiar, el formato que estábamos usando antes era incorrecto sintaxis: generalmente se usa para un método .where.

def check_exists 
    @tag = Tag.find_by_name_and_user_id(self.name, self.user_id) 
    if @tag != nil 
     #return false 
     #[email protected] 
    end 
    end 

El problema ahora es este, puedo saber si la etiqueta ya existe. ¿Pero entonces, qué? Si voy con la opción de devolución falsa, hay un error al crear el mensaje, y el registro de combinación no se crea ... La otra opción "self = @ tag" obviamente no funciona.

+3

Tiempo de revisión de código: Cuando se nombran rutinas, desea usar un nombre que sea indicativo de lo que hace. Llamar a un método "check_exists" cuando en realidad podría estar creando registros podría hacer que sea más difícil encontrar "de dónde vino ese registro". una vez que tienes un montón de código en una aplicación. Tal vez "get_tag" o "find_or_create_tag" sería mejor? Este tipo de cosas sutiles se suman en una aplicación y determinan su legibilidad y mantenibilidad a lo largo del tiempo. –

+0

El tiempo de espera del servidor probablemente no esté relacionado con el código. Verifique si realmente se está ejecutando y, si está en otra máquina, si tiene conectividad. –

+0

Realmente está sucediendo en mi entorno local. El servidor se congela cada vez que intento crear una etiqueta incluso manualmente a través del administrador de rieles. – Elliot

Respuesta

11

Le resultará difícil hacerlo desde el modelo Tag. Parece que lo que desea es actualizar el mensaje utilizando atributos anidados, así:

post = Post.create 
post.update_attributes(:tags_attributes=>{"0"=>{:name=>"fish",:user_id=>"37"}}) 

Esto es en realidad muy simple de hacer mediante el uso de un método atributo incubadora virtual:

class Post < AR::Base 
    has_many :tags 

    def tags_attributes=(hash) 
    hash.each do |sequence,tag_values| 
     tags << Tag.find_or_create_by_name_and_user_id(tag_values[:name],\ 
     tag_values[:user_id]) 
    end 
    end 

> post = Post.create 
> post.update_attributes(:tags_attributes=>{"0"=>{:name=>"fish",:user_id=>"37"}}) 
> Tag.count # => 1 
# updating again does not add dups 
> post.update_attributes(:tags_attributes=>{"0"=>{:name=>"fish",:user_id=>"37"}}) 
> Tag.count # => 1 
+1

Muchas gracias Zetetic, ¡estuve luchando todo el día! – Elliot

+0

Encontré esto después de 5 horas de búsqueda, eres mi héroe – cubny

8

¿Sabía que hay una función find_by_or_create_by_ integrada en Rails, ¿verdad?

# No 'Summer' tag exists 
Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer") 

# Now the 'Summer' tag does exist 
Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer") 

http://api.rubyonrails.org/classes/ActiveRecord/Base.html (bajo buscadores dinámicos basados ​​en atributos)

+0

¿Cómo puedo usar esto si estoy comprobando basado en el nombre y el ID de usuario? ¿Y dónde debería usar esto? En el método check_exists? – Elliot

+0

Sí, ver Rob Di Marco ha proporcionado un ejemplo simple, ver a continuación. – Bjorn

6

Usted desea utilizar el método mágico find_or_create_by

def check_exists 
    tag = Tag.find_or_create_by_name_and_user_id(:name => self.name, :user_id => current_user.id) 
end 

Mira la ActiveRecord::Base documentos para obtener más información

+0

obteniendo un tiempo de espera del servidor usando este – Elliot

+1

El problema es que el método check_exists se ejecuta como una llamada before_create, pero está tratando de crear la etiqueta. Entonces, si desea usar Tag.find_or_create_by_name_and_user_id, debe hacer eso desde el código de llamada (¿su controlador tal vez?). Si está tratando de hacer cumplir que la etiqueta solo aparece una vez con la combinación nombre/ID de usuario, revise validates_uniqueness_of que se asegurará de esto. –

+0

Hola Rob, tiene sentido, acabo de actualizar la pregunta. La forma en que se crean las etiquetas parece un poco complicada (usé un ep de transmisión de ráfagas - como se relacionó anteriormente), me pregunto si debería usar find_or_create en el método que agregué arriba. – Elliot

0

probar esto

def check_exists 
    tag = Tag.where(:name => self.name, :user_id => current_user.id).first 
    tag = Tag.new({:name => self.name, :user_id => current_user.id}) unless tag 
    end 

uso Tag.new en lugar de Tag.create

+0

Tag.where devolverá la colección, puede usar '' Tag.where (: name => self.name,: user_id => current_user.id) .first'' – Pavel

+0

Sí, tiene razón. Copié la parte pegada de la pregunta y no verifiqué esa línea. hará la edición. – rubyprince

4

La pregunta que originalmente pregunté puso bastante distorsionada por el final. Entonces lo estoy separando.

Las personas que están tratando de hacer lo que le pedía inicialmente puede intentar esto:

before_create :check_tag exists 

private 

def check_tag_exists 
    @tag = Tag.find_by_name_and_user_id(self.name, self.user_id) 
    if @tag != nil 
     # 
    end 
    end 

Esto le permitirá comprobar si ya se ha creado su registro. Cualquier lógica adicional que puedas incluir en eso si es una declaración.

+1

Buen trato volviendo para aclarar. – Mosselman

-2

donde devuelve una vacíe ActiveRecord al no encontrar ninguna coincidencia.

3

Creo que las otras respuestas son un poco anticuadas. Así es como probablemente debería lograr esto para Rails 4

tag = Tag.first_or_initialize(:name => self.name, :user_id => current_user.id) 
if !tag.new_record? 
    tag.id = self.id 
    tag.save 
end 
Cuestiones relacionadas