update rails query left joins includes has_and_belongs_to_many active ruby-on-rails activerecord

ruby-on-rails - left - rails sql query



Obtener nombres de columnas con ActiveRecord (4)

AR proporciona un método #column_names que devuelve una matriz de nombres de columna

¿Hay alguna manera de obtener el nombre de las columnas reales con ActiveRecord?

Cuando llamo find_by_sql o select_all con un join, si hay columnas con el mismo nombre, el primero queda anulado:

select locations.*, s3_images.* from locations left join s3_images on s3_images.imageable_id = locations.id and s3_images.imageable_type = ''Location'' limit 1

En el ejemplo anterior, obtengo lo siguiente:

#<Location id: 22, name: ... >

Donde id es el de la última s3_image. select_rows es lo único que funcionó como se esperaba:

Model.connection.select_rows("SELECT id,name FROM users") => [["1","amy"],["2","bob"],["3","cam"]]

Necesito obtener los nombres de campo para las filas de arriba. Esta publicación se acerca a lo que quiero pero parece obsoleta (fetch_fields ya no parece existir ¿Cómo se obtienen las filas y las columnas en el resultado de una consulta con ActiveRecord? )

El método de unión ActiveRecord crea múltiples objetos. Estoy tratando de lograr el mismo resultado que "incluye" volvería pero con una combinación izquierda.

Estoy intentando devolver un montón de resultados (y, a veces, tablas completas) esta es la razón por la que incluye no se ajusta a mis necesidades.


Así es como funciona el método de inspección del registro activo: solo enumera las columnas de la tabla del modelo. Los atributos todavía están allí, aunque

record.blah

devolverá el atributo blah, incluso si proviene de otra tabla. También puedes usar

record.attributes

para obtener un hash con todos los atributos.

Sin embargo, si tiene varias columnas con el mismo nombre (p. Ej., Ambas tablas tienen una columna de id.), El registro activo simplemente mezcla las cosas, ignorando el nombre de la tabla. Deberá alias los nombres de las columnas para que sean únicas.


De acuerdo, he querido hacer algo que sea más eficiente por un tiempo.

Tenga en cuenta que para muy pocos resultados, incluya los trabajos muy bien. El siguiente código funciona mejor cuando tienes muchas columnas a las que te gustaría unirte.

Para facilitar la comprensión del código, primero trabajé en una versión fácil y la amplié.

Primer método:

# takes a main array of ActiveRecord::Base objects # converts it into a hash with the key being that object''s id method call # loop through the second array (arr) # and call lamb (a lambda { |hash, itm| ) for each item in it. Gets called on the main # hash and each itm in the second array # i.e: You have Users who have multiple Pets # You can call merge(User.all, Pet.all, lambda { |hash, pet| hash[pet.owner_id].pets << pet } def merge(mainarray, arr, lamb) hash = {} mainarray.each do |i| hash[i.id] = i.dup end arr.each do |i| lamb.call(i, hash) end return hash.values end

Luego noté que podemos tener tablas "directas" (relaciones nxm)

merge_through! aborda este problema:

# this works for tables that have the equivalent of # :through => # an example would be a location with keywords # through locations_keywords # # the middletable should should return as id an array of the left and right ids # the left table is the main table # the lambda fn should store in the lefthash the value from the righthash # # if an array is passed instead of a lefthash or a righthash, they''ll be conveniently converted def merge_through!(lefthash, righthash, middletable, lamb) if (lefthash.class == Array) lhash = {} lefthash.each do |i| lhash[i.id] = i.dup end lefthash = lhash end if (righthash.class == Array) rhash = {} righthash.each do |i| rhash[i.id] = i.dup end righthash = rhash end middletable.each do |i| lamb.call(lefthash, righthash, i.id[0], i.id[1]) end return lefthash end

Así es como lo llamo:

lambmerge = lambda do |lhash, rhash, lid, rid| lhash[lid].keywords << rhash[rid] end Location.merge_through!(Location.all, Keyword.all, LocationsKeyword.all, lambmerge)

Ahora para el método completo (que hace uso de merge_through)

# merges multiple arrays (or hashes) with the main array (or hash) # each arr in the arrs is a hash, each must have # a :value and a :proc # the procs will be called on values and main hash # # :middletable will merge through the middle table if provided # :value will contain the right table when :middletable is provided # def merge_multi!(mainarray, arrs) hash = {} if (mainarray.class == Hash) hash = mainarray elsif (mainarray.class == Array) mainarray.each do |i| hash[i.id] = i.dup end end arrs.each do |h| arr = h[:value] proc = h[:proc] if (h[:middletable]) middletable = h[:middletable] merge_through!(hash, arr, middletable, proc) else arr.each do |i| proc.call(i, hash) end end end return hash.values end

Así es como uso mi código:

def merge_multi_test() merge_multi!(Location.all, [ # each one location has many s3_images (one to many) { :value => S3Image.all, :proc => lambda do |img, hash| if (img.imageable_type == ''Location'') hash[img.imageable_id].s3_images << img end end }, # each location has many LocationsKeywords. Keywords is the right table and LocationsKeyword is the middletable. # (many to many) { :value => Keyword.all, :middletable => LocationsKeyword.all, :proc => lambda do |lhash, rhash, lid, rid| lhash[lid].keywords << rhash[rid] end } ]) end

Puede modificar el código si desea cargar atributos que son de uno a varios (como una ciudad es una ubicación). Básicamente, el código anterior no funcionará porque tendrá que recorrer el hash principal y establecer el ciudad desde el segundo hash (no hay tabla "city_id, location_id"). Puede invertir la ciudad y la ubicación para obtener todas las ubicaciones en el hash de la ciudad y luego extraer de nuevo. No necesito ese código todavía, así que lo salté =)


dos opciones

Model.column_names

o

Model.columns.map(&:name)

Modelo de ejemplo llamado Conejo con nombre de columnas, edad, on_facebook

Rabbit.column_names Rabbit.columns.map(&:name)

devoluciones

["id", "name", "age", "on_facebook", "created_at", "updated_at"]