Подтвердить что ты не робот

Получить имена колонок с помощью ActiveRecord

Есть ли способ получить фактическое имя столбца с помощью ActiveRecord?

Когда я вызываю find_by_sql или select_all с соединением, если есть столбцы с тем же именем, первый получает переопределение:

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

В приведенном выше примере я получаю следующее:

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

Где id - идентификатор последнего s3_image. select_rows - единственное, что работало, как ожидалось:

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

Мне нужно получить имена полей для строк выше. Это сообщение приближается к тому, что я хочу, но выглядит устаревшим (fetch_fields больше не существует Как вы получаете строки и столбцы в результате запроса с помощью ActiveRecord?)

Метод объединения ActiveRecord создает несколько объектов. Я пытаюсь достичь того же результата, "включая", вернется, но с левым соединением.

Я пытаюсь вернуть много результатов (а иногда и целых таблиц), поэтому включение не подходит моим потребностям.

4b9b3361

Ответ 1

Active Record предоставляет метод #column_names, который возвращает массив имен столбцов.

Пример использования: User.column_names

Ответ 2

две опции

Model.column_names

или

Model.columns.map(&:name)

Пример Модель с именем Rabbit с именами столбцов, возраста, on_facebook

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

возвращает

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

Ответ 3

Это работает только активный метод проверки записей: он отображает только столбец из таблицы модели. Атрибуты все еще существуют, но

record.blah

вернет атрибут blah, даже если он из другой таблицы. Вы также можете использовать

record.attributes

чтобы получить хэш со всеми атрибутами.

Однако, если у вас есть несколько столбцов с одинаковым именем (например, обе таблицы имеют столбец id), то активная запись просто объединяет вещи вместе, игнорируя имя таблицы. Вам нужно будет псевдонимы имен столбцов, чтобы сделать их уникальными.

Ответ 4

Хорошо, я уже давно хотел сделать что-то более эффективное.

Обратите внимание, что за очень небольшое количество результатов включите работы просто отлично. Код ниже работает лучше, если у вас есть много столбцов, к которым вы хотите присоединиться.

Чтобы упростить понимание кода, я сначала разработал легкую версию и расширил ее.

Первый метод:

# takes a main array of ActiveRecord::Base objects
# converts it into a hash with the key being that object 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

Затем я заметил, что мы можем иметь "сквозные" таблицы (отношения nxm)

merge_through! решает эту проблему:

  # 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

Вот как я его называю:

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

Теперь для полного метода (который использует 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

Вот как я использую свой код:

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

Вы можете изменить код, если хотите ленивые атрибуты загрузки, которые являются одними для многих (например, город для местоположения). В принципе, код выше не будет работать, потому что вам придется перебирать основной хэш и установите город из второго хэша (таблицы "city_id, location_id" нет). Вы можете изменить город и местоположение, чтобы получить все местоположения в хеше города, а затем извлечь их обратно. Мне еще не нужен этот код, поэтому я пропустил его =)