1

A has_many B の 2 つのモデルがあります。関連付けられた B を含む A をロードすると、次のようになります。

a = A.find(:first, include: :bs)

a.inspect次の属性のみを表示します。

 => "#<A id: 1, name: \"Test\", created_at: \"2012-07-02 21:50:32\", updated_at: \"2012-07-02 21:50:32\">"

a.inspect関連するすべてを表示するようにするにはどうすればよいa.bsですか?

4

2 に答える 2

1

デフォルトではそれを行うことはできません。オブジェクトの検査で多くの問題や副作用が発生する可能性があります。inspectただし、次のような方法で自分自身を拡張することができます。

class A < ActiveRecord::Base
  ...
  def inspect
    [super, bs.inspect].join("\n")
  end
end

ただし、インスタンスbsを検査するたびに強制的にロードされるため、それほど賢くはありません。Aだから多分あなたはもっと賢くなりたいと思うでしょうそしてこのようなことをしたいでしょう:

def inspect
  [super, bs.loaded? ? bs.inspect : nil].compact.join("\n")
end

bsこれは、すでにプリロードされているかどうかのみを検査します(:includeたとえば)。

super_inspectまたは、すべてを自動的に実行する代わりに作成したい場合もあります。ActiveRecord::Base次のように拡張できます。

class ActiveRecord::Base
  def deep_inspect
    ([inspect] + self.class.reflect_on_all_associations.map { |a|
      self.send(a.name).inspect
    }).compact.join("\n  ")
  end
end

これにより、とのすべての関連付けが自動的に検索されreflect_on_all_associations、関連付けが読み込まれると、それが呼び出さinspectれます。

これで、上記のコードを変更できますが、独自のカスタマイズされた検査を作成するか、必要に応じて現在の検査を拡張するだけです。少しのコードで何でも可能です。

少しスマートな更新バージョンの例を次に示します。

class ActiveRecord::Base
  def deep_inspect
    ([inspect] + self.class.reflect_on_all_associations.map { |a|
      out = ""
      assoc = self.send(a.name)
      # Check for collection
      if assoc.is_a?(ActiveRecord::Associations::CollectionProxy)
        # Include name of collection in output
        out += "\n#{assoc.name.pluralize}:\n"
        out += self.send(a.name).to_a.inspect
      else
        out += self.send(a.name).inspect
      end
      out
    }).compact.join("\n  ")
  end
end
于 2012-07-03T20:59:09.797 に答える
0

@Casperからの回答と同じ行に沿って、依存関係チェーンのすべての関連付けをマーシャリングするヘルパーメソッドを次に示します。

# app/models/application_record.rb
#
#   placing the helper in the ApplicationRecord superclass
#   allows all application models to inherit the helper


class ApplicationRecord < ActiveRecord::Base
  def self.marshal

    # collect the names of the objects associations
    single_associations = self.class.reflect_on_all_associations(:has_one ).map {|x| x.name}
    plural_associations = self.class.reflect_on_all_associations(:has_many).map {|x| x.name}

    # serialize the object as a JSON-compatible hash
    self.as_json.merge(

      # merge in a hash containing each `has_one` association via recursive marshalling
      #   the resulting set of associated objects are merged into
      #   the original object's serialized hash, each keyed by the name of the association
        single_associations.reduce({}) { |memo, assoc| memo.merge({ assoc => self.send(assoc).marshal }) }.as_json
    ).merge(

      # merge in the `has_many` associations
      #   the resulting set of associated collections must then be processed
      #   via mapping each collection into an array of singular serialized objects
        plural_associations.reduce({}) { |memo, assoc| memo.merge({ assoc => self.send(assoc).map {|item| item.marshal } }) }.as_json
    )
  end
end

次に、次のように呼び出して、このヘルパー メソッドを呼び出すことができます。

Marshal.serialize a

これは、実際にはオブジェクトをハッシュ構造にシリアライズしているため、インスペクションとまったく同じではありませんが、同様の情報が得られます。


考えられる関連付けは、単数の関連付け (単一のターゲット オブジェクトを参照する) と複数の関連付け ( ActiveRecord CollectionProxyオブジェクト、つまり列挙可能)の 2 つのグループに分けられることに注意してください。関連付けられたオブジェクトをハッシュとしてシリアル化するため、各has_many関連付けは個別にシリアル化されたオブジェクトのコレクションとして解析する必要があります (たとえば、コレクション内の各関連付けをシリアル化された形式としてマップします)。

belongs_to関連付けを両方向にマッピングすると、すぐに循環依存関係グラフが作成されるため、関連付けは無視する必要があります。代わりに「所属の連鎖」に沿ってマーシャリングしたい場合は、次のようにすることができます

def self.trace
    parent_associations = obj.class.reflect_on_all_associations(:belongs_to).map {|x| x.name}

    obj.as_json.merge single_associations.reduce({}) { |memo, assoc| memo.merge({ assoc => obj.send(assoc).trace }) }.as_json
end

于 2020-01-23T08:02:57.390 に答える