47

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 の ID です。select_rows は、期待どおりに機能した唯一のものです。

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

上記の行のフィールド名を取得する必要があります。この投稿は私が望むものに近づいていますが、時代遅れに見えます (fetch_fields はもう存在しないようですActiveRecord を使用してクエリの結果で行と列を取得するにはどうすればよいですか? )

ActiveRecord join メソッドは、複数のオブジェクトを作成します。「includes」が返すのと同じ結果を達成しようとしていますが、左結合を使用しています。

大量の結果 (場合によってはテーブル全体) を返そうとしていますが、これが include が私のニーズに合わない理由です。

4

4 に答える 4

84

Active Record は、#column_names列名の配列を返すメソッドを提供します。

使用例:User.column_names

于 2012-06-13T12:40:04.260 に答える
21

2 つのオプション

Model.column_names

また

Model.columns.map(&:name)

列 name、age、on_facebook を持つ Rabbit という名前のモデルの例

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

戻り値

["id", "name", "age", "on_facebook", "created_at", "updated_at"] 
于 2015-03-05T04:01:09.980 に答える
5

これは、アクティブ レコードの inspect メソッドが機能する方法です。モデルのテーブルの列のみを一覧表示します。属性はそのままだけど

record.blah

別のテーブルからのものであっても、何とか属性を返します。使用することもできます

record.attributes

すべての属性を持つハッシュを取得します。

ただし、同じ名前の列が複数ある場合 (たとえば、両方のテーブルに id 列がある場合)、アクティブ レコードは、テーブル名を無視して物事をマッシュアップするだけです。

于 2012-06-13T12:47:35.100 に答える
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

1対多の属性を遅延ロードする場合はコードを変更できます(都市は場所になります)。基本的に、メインハッシュをループして設定する必要があるため、上記のコードは機能しません。 2番目のハッシュからの都市(「city_id、location_id」テーブルはありません)。都市と場所を逆にして、都市ハッシュ内のすべての場所を取得してから抽出することができます。そのコードはまだ必要ないのでスキップしました=)

于 2012-06-13T14:56:13.130 に答える