2012-06-13 2 views
30

Есть ли способ получить фактическое имя столбца с помощью 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 больше не существует How do you get the rows and the columns in the result of a query with ActiveRecord?)

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

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

+1

Вы можете получить все столбцы модели ActiveRecord, используя: ** Model.columns.map (&: name) **, но я не уверен, что это то, что вы хотите. – MurifoX

+0

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

ответ

54

AR обеспечивает #column_names метод, который возвращает массив имен столбцов

+6

для любого, кто приходит сюда в будущем, этот метод устарел http://apidock.com/rails/ActiveRecord/Base/column_names/class – pahnin

+0

Спасибо за обмен, @pahnin; было бы здорово, если бы вы (или кто-либо еще) могли рекомендовать альтернативы. – Abdo

+16

column_names() по-прежнему доступен для классов ActiveRecord, он только что был перемещен в другой модуль. – nocache

4

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

record.blah 

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

record.attributes 

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

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

+0

Спасибо за ваш ответ. Я пробовал наложение псевдонимов, но ничего не изменил в AR. Это всегда одинаковые результаты. Существует ли какое-либо конкретное наложение, которое ожидает AR? – Abdo

+0

каждое имя столбца должно быть уникальным, например. выберите table1. *, table2.id как t2_id, table2.name как t2_name и т. д. AR не будет делать ничего умного с этим, но он остановит все столкновение –

+0

Это означает, что я должен был бы перечислить каждый дубликат столбца ... Очень утомительно :-( – Abdo

0

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

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

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

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

# 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 

Я тогда заметил, что мы можем иметь "через" таблицы (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» нет). Вы можете изменить город и местоположение, чтобы получить все местоположения в хеше города, а затем извлечь их обратно. Мне не нужен этот код еще, так что я пропустил это =)

10

два варианта

Model.column_names 

или

Model.columns.map(&:name) 

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

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

возвращение

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