ruby-on-rails - inner - rails query where
default_scope y asociaciones (5)
Supongamos que tengo un modelo de publicación y un modelo de comentario. Usando un patrón común, Publicar has_many Comentarios.
Si el comentario tiene un conjunto default_scope:
default_scope where("deleted_at IS NULL")
¿Cómo recupero fácilmente TODOS los comentarios en una publicación, independientemente del alcance? Esto produce resultados inválidos:
Post.first.comments.unscoped
Que genera las siguientes consultas:
SELECT * FROM posts LIMIT 1;
SELECT * FROM comments;
En lugar de:
SELECT * FROM posts LIMIT 1;
SELECT * FROM comments WHERE post_id = 1;
Corriendo:
Post.first.comments
Produce:
SELECT * FROM posts LIMIT 1;
SELECT * FROM comments WHERE deleted_at IS NULL AND post_id = 1;
Entiendo el principio básico de la eliminación sin ámbito de todos los ámbitos existentes, pero ¿no debería ser consciente y mantener el alcance de la asociación?
¿Cuál es la mejor manera de sacar TODOS los comentarios?
De hecho, este es un problema muy frustrante que viola el principio de la menor sorpresa.
Por ahora, puedes escribir:
Comment.unscoped.where(post_id: Post.first)
Esta es la solución más elegante / simple de IMO.
O:
Post.first.comments.scoped.tap { |rel| rel.default_scoped = false }
La ventaja de este último:
class Comment < ActiveRecord::Base
# ...
def self.with_deleted
scoped.tap { |rel| rel.default_scoped = false }
end
end
Entonces puedes hacer cosas divertidas:
Post.first.comments.with_deleted.order(''created_at DESC'')
Desde Rails 4, Model.all devuelve un ActiveRecord :: Relation, en lugar de una matriz de registros. Así que puedes (y deberías) usar all
lugar del scoped
:
Post.first.comments.all.tap { |rel| rel.default_scoped = false }
Por alguna extraña razón,
Comment.unscoped { Post.last.comments }
incluye el default_scope
del Comment
,
sin embargo,
Comment.unscoped { Post.last.comments.to_a }
Comment.unscoped { Post.last.comments.order }
No incluya el default_scope
del Comment
.
Experimenté esto en una sesión de rails console
Rails 3.2.3
con Rails 3.2.3
.
Rails 4.1.1
Comment.unscope(where: :deleted_at) { Post.first.comments }
O
Comment.unscoped { Post.first.comments.scope }
Tenga en cuenta que agregué .scope
, parece que este bloque debería devolver el tipo de ActiveRecord_AssociationRelation
(lo que hace .scope
) no ActiveRecord_Associations_CollectionProxy
(sin un .scope
)
with_exlusive_scope
está en desuso a partir de Rails 3. Ver este commit .
Antes (carriles 2):
Comment.with_exclusive_scope { Post.find(post_id).comments }
Después (Rieles 3):
Comment.unscoped { Post.find(post_id).comments }
class Comment
def post_comments(post_id)
with_exclusive_scope { find(all, :conditions => {:post_id => post_id}) }
end
end
Comment.post_comments(Post.first.id)