Dado un conjunto de objetos ActiveRecord, ¿puedo recopilar fácilmente sus relaciones a través de una llamada de método?
-
03-07-2019 - |
Pregunta
Digamos que tengo el siguiente código:
@sites = Site.find(session[:sites]) # will be an array of Site ids
@languages = Language.for_sites(@sites)
for_sites es un named_scope en el modelo de idioma que devuelve los idiomas asociados con esos sitios, y los idiomas se asocian con los sitios que utilizan has_many. El objetivo es que @languages ??tenga una variedad distinta de los idiomas asociados con los sitios.
En lugar de llamar al objeto de idioma en la segunda línea, lo ideal sería decir
@sites.languages
y que me devuelvan la misma lista. ¿Hay alguna manera de hacerlo limpiamente en Rails 2.1 (o edge)? Sé que las asociaciones y los ámbitos con nombre pueden extender el objeto de matriz para tener atributos, pero a menos que me falte algo que no se aplique aquí. Cualquier complemento que haga esto sería bienvenido, no tiene que estar en el núcleo.
Solución
Podría ampliar la matriz devuelta por Site.find.
class Site
def find(*args)
result = super
result.extend LanguageAggregator if Array === result
result
end
end
module LanguageAggregator
def languages
Language.find(:all, :conditions => [ 'id in (?)', self.collect { |site| site.id } ])
end
end
Otros consejos
¿Por qué no usar named_scopes para ambos?
class Site
named_scope :sites, lambda{|ids| :conditions => "id in (#{ids.join(',')})"}
named_scope :languages, :include => :languages ... (whatever your named scope does)
end
llamada:
Site.sites(session[:sites]).languages
o, si quieres recuperar los objetos de lenguaje
Site.sites(session[:sites]).languages.collect{|site| site.languages}.flatten
También puedes hacerlo directamente en el objeto Idioma. Estoy usando: se une porque Rails 2.1 se divide y se incluye en dos consultas, lo que significa que no podemos usar sitios en las: condiciones
class Language
named_scope :for_sites, lambda{|site_ids| :joins => 'inner join sites on languages.site_id = sites.id' :conditions => "sites.id in (#{site_ids.join(',')})"}
end
llamada:
Language.for_sites(session[:sites])
En ambos ejemplos, asumí que la sesión [: sitios] está completamente controlada por usted y no está sujeta a inyección SQL. Si no es así, asegúrese de tratar con la limpieza de las ID
Su variable de instancia @sites es un objeto Array y no un Sitio, por lo que no creo que se pueda usar named_scope. Sin embargo, puede abrir la clase Array para lograr este efecto (sí)
class Array
def languages
...
end
end
Si agregó un has_many
o has_and_belongs_to_many
vinculando los idiomas a los sitios, podría usar una inclusión y hacer algo como esto:
Site.find( :all, :conditions =>{:id => session[:sites]}, :include => :languages )
Puedes hacer un ámbito con nombre para hacer lo siguiente: id = > sesión [: sitios] cosa, por ejemplo:
class Site
named_scope :for_ids, lambda{ |x| {:conditions => {:id => x }
end
y luego hazlo
Site.for_ids(session[:sites]).find(:all, :include => :languages)
Espero que esto te dé algunas ideas