2010-10-15 15 views
9

Creo que esto es un error en Rails 3. Espero que alguien aquí pueda dirigirme en la dirección correcta. El código publicado a continuación es puramente ilustrativo de este problema. Espero que esto no confunda el problema.default_scope breaks (update | delete | destroy) _all en algunos casos

Dado que tengo un modelo de publicación y un modelo de comentario. Publicar has_many Comentarios, y Comentario pertenece_ a Publicar.

Con un default_scope establecido en el modelo Post, definiendo las relaciones joins() y where(). En este caso, donde() depende de joins().

Normalmente, las publicaciones no dependen de los comentarios. De nuevo, solo quiero dar un ejemplo simple. Esto podría ser cualquier caso cuando where() depende de joins().

class Post < ActiveRecord::Base 
    has_many :comments, :dependent => :destroy 

    default_scope joins(:comments).where("comments.id < 999") 
end 

class Comment < ActiveRecord::Base 
    belongs_to :post, :counter_cache => true 
end 

Ejecutar el siguiente comando:

Post.update_all(:title => Time.now) 

Produce la siguiente consulta, y en última instancia tiros ActiveRecord :: StatementInvalid:

UPDATE `posts` SET `title` = '2010-10-15 15:59:27' WHERE (comments.id < 999) 

Una vez más, update_all, delete_all, destroy_all se comportan de la misma manera . Descubrí este comportamiento cuando mi aplicación se quejó cuando intentaba actualizar counter_cache. Que finalmente se profundiza en update_all.

Respuesta

4

I ran into this as well.

Si tiene

class Topic < ActiveRecord::Base 
    default_scope :conditions => "forums.preferences > 1", :include => [:forum] 
end 

y haces un

Topic.update_all(...) 

que va a fallar con

Mysql::Error: Unknown column 'forums.preferences' in 'where clause' 

El trabajo alrededor de esto es:

Topic.send(:with_exclusive_scope) { Topic.update_all(...) } 

Usted puede parchear este mono usando el código (y que lo requieran en environment.rb o en otro lugar)

module ActiveRecordMixins 
    class ActiveRecord::Base 
    def self.update_all!(*args) 
     self.send(:with_exclusive_scope) { self.update_all(*args) } 
    end 
    def self.delete_all!(*args) 
     self.send(:with_exclusive_scope) { self.delete_all(*args) } 
    end 
    end 
end 

final

A continuación, sólo se update_all! o eliminar_todo! cuando tiene un alcance predeterminado.

+0

Hola, Ben, ¿abriste un problema sobre esto? –

1

También puede hacer esto en el nivel de clase, sin crear nuevos métodos, así:

def self.update_all(*args) 
    self.send(:with_exclusive_scope) { super(*args) } 
end 

def self.delete_all(*args) 
    self.send(:with_exclusive_scope) { super(*args) } 
end 
7

tuve este problema también, pero realmente se necesita para ser capaz de utilizar update_all con condiciones complejas en el default_scope (por ejemplo, sin el alcance predeterminado, la carga ansiosa es imposible, y pegar un ámbito con nombre literalmente en todas partes no es nada divertido).He abierto una petición de atracción aquí con mi dosis:

https://github.com/rails/rails/pull/8449

Para delete_all He criado a un error si hay una condición de unión para que sea más evidente lo que tiene que hacer (en lugar de simplemente tirar la unión condición y ejecutando el delete_all en todo, obtienes un error).

No estoy seguro de lo que los chicos de los rieles van a hacer con mi solicitud de extracción, pero pensé que era relevante para esta discusión. (Además, si necesita corregir este error, puede probar mi sucursal y publicar un comentario en la solicitud de extracción.)

0

No creo que lo llamaría un error. El comportamiento me parece lo suficientemente lógico, aunque no inmediatamente obvio. Pero elaboré una solución SQL que parece funcionar bien. Usando su ejemplo, sería:

class Post < ActiveRecord::Base 
    has_many :comments, :dependent => :destroy 

    default_scope do 
    with_scope :find => {:readonly => false} do 
     joins("INNER JOIN comments ON comments.post_id = posts.id AND comments.id < 999") 
    end 
    end 
end 

En realidad estoy usando la reflexión para que sea más robusta, pero lo anterior pone la cruz idea. Mover la lógica WHERE a JOIN asegura que no se aplicará en lugares inapropiados. La opción :readonly es contrarrestar el comportamiento predeterminado de Rails de hacer joins 'd objetos de solo lectura.

Además, sé que algunas personas se burlan del uso de default_scope. Pero para aplicaciones multi-tenant, es un ajuste perfecto.

Cuestiones relacionadas