Wednesday 9 May 2007

Active Record find

Actualmente me encuentro desarrollando una aplicación WEB que dispone de un API REST y una parte estándard. A la hora de desarrollar las consultas a los modelos, me he encontrado con que algunas de estas son muy complejas y tiendo a pensarlas en SQL directamente, ya que es un lenguaje que da un gran rendimiento y flexibilidad.

ActiveRecord::Base nos proporciona dos métodos para realizar estas consultas, find y find_by_sql. En el caso del segundo la opción está clara: insertar directamente el SQL que he pensado para obtener solo lo que yo quiero obtener y con el formato que yo necesito. En el caso del primero, solo con echar un vistazo al API podemos ver que tiene un gran número de posibilidades, parámetros, etc.. que lo convierten en una opción muy poderosa para realizar las consultas. Asi pues, me surge una duda ¿Cual de ellas usar?

Esta pregunta, que en principio parece muy sencilla, no lo es tanto. Si controlas SQL tenderás a hacer uso de find_by_sql ya que te abstraeras de aprender las opciones y posibilidades de ActiveRecord y trabajarás con algo conocido. Pero precisamente esto es algo que no deberíamos hacer. Bajo mi punto de vista, tendríamos que usar las ventajas que nos da Rails evitando usar SQL directamente.

Diseñamos la BD, definimos los modelos de las tablas, declaramos las relaciones entre ellos. CONTROLAMOS la estructura de nuestros datos. Imaginemos que tenemos una tabla Articles y otra tabla Tags. Definimos una relación n-to-n con sendas directivas :has_and_belongs_to. Bien, pues en el caso en que queramos obtener todas las tags de un artículo, podemos bien escribir un SQL que obtenga la información de las distintas tablas o podemos dejar hacer a ActiveRecord, ya que por el hecho de tener esta relación, es capaz de proporcionarnos esta información automáticamente con el siguiente código.

article = Article.find(article_id)
article.tags.each do |tag|
puts tag.tag_name
end

Mantenemos el código sencillo. Usamos los convencionalimos de Rails. Resulta extremadamente sencillo de interpretar. EVITAMOS MEZCLAR LENGUAJES.

1 comment:

Anonymous said...

La opción existe para aprovecharla cuando realmente la necesitas. La gente de Rails es bastante rígida en sus recomendaciones: Usa todo al estilo Rails siempre que puedas, guárdate las excepciones para casos excepcionales. Y sí, mayormente funicona muy bien - Por ejemplo, hice mi primer caso de uso de find_by_sql al meter una relación has-many a una tabla con contenido binario, con muchos registros que pasan del mega. Estar enviando todos los datos a cada Document.find(:all, :conditions => ['presentation_id = ?', id]) era excesivo. ¿Mi salida? En Document agregué:
def Document.find (*args)
select = self.columns.map {|c| c.name}.select{|c| c != 'data'}.join(', ')

if args[-1].is_a?(Hash)
if args[1].has_key? :select
select = args[-1][:select]
else
args[-1][:select] = select
end
else
args << {:select => select}
end

super(*args)
end

def size
# As it is not a field in the DB, it gets here as a string
Document.find(self.id, :select => 'length(data)').length.to_i
end

def data
return self[:data] if self.attributes.has_key? 'data'

doc = Document.find_by_sql(['SELECT * FROM documents WHERE id = ?', self.id])[0]
doc[:data]
end

# IMPORTANT THING TO REMEMBER:
#
# Contrary to most Rails conventions, data is saved AS SOON AS IT IS RECEIVED
# in order to free us a bit from the pain of getting the full file data at
# every instantiation.
def data=(value)
return super(value) if self.attributes.has_key? 'data'

doc = Document.find_by_sql(['SELECT * FROM documents WHERE id = ?', self.id])[0]
doc.data=value

doc.save!
end

...Y créeme, un _gran_ problema de rendimiento menos.
(disculpa el indentado o falta de, parece que a Blogger no le gusta ningún pedazo de HTML que me permita ponerlo sensiblemente)