1

Component(#70354292616060) expected, got String(#70354278277000)以下のオブジェクト「Machine」の作成時にActiveRecord::AssociationTypeMismatchを取得しました。

目的: 他のモデルを参照するモデルに「属性」を割り当ててみます。

質問: この種の操作のベスト プラクティスは何か、専門家から聞きたいです。

設定

Rails 3.2.12 / Ruby 1.9.3-p194:

モデル:

class Machine < ActiveRecord::Base
  attr_accessible :cpu, :name, :ram

  belongs_to :cpu, class_name: "Component", foreign_key: "component_id"
  belongs_to :ram, class_name: "Component", foreign_key: "component_id"
end

class Component < ActiveRecord::Base
  attr_accessible :name
  has_many :machines
end

マシンのビュー:

  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :cpu %><br />
    <%= f.collection_select :cpu, Component.all, :id, :name %>
  </div>
  <div class="field">
    <%= f.label :ram %><br />
    <%= f.collection_select :ram, Component.all, :id, :name %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>

手順

  1. id:1 name:"cpu1"&などのコンポーネントにいくつかのオブジェクトを作成しますid:2 name:"ram1"
  2. machines_path に移動し、プルダウンから cpu1 と ram1 を選択して Machine を作成します。
  3. 送信時に次のエラーが発生しましたComponent(#70354292616060) expected, got String(#70354278277000)

View で :cpu と :ram を :cpu_id と :ram_id に変更しようとしました。その後、エラーなくモデルを作成できます。ただし、マシンから直接 CPU と RAM にアクセスすることはできません。

1.9.3-p194 :001 > Machine.first
  Machine Load (0.1ms)  SELECT "machines".* FROM "machines" LIMIT 1
 => #<Machine id: 2, name: "test", cpu_id: 1, ram_id: 1, created_at: "2013-05-06     16:42:47", updated_at: "2013-05-06 16:42:47"> 
1.9.3-p194 :002 > Machine.first.cpu
  Machine Load (0.2ms)  SELECT "machines".* FROM "machines" LIMIT 1
 => nil 
1.9.3-p194 :003 > Machine.first.ram
  Machine Load (0.2ms)  SELECT "machines".* FROM "machines" LIMIT 1
 => nil 

したがって、次のモデルメソッドを作成する必要がありました

  class Machine < ActiveRecord::Base

  (snip)

  def cpu
    Component.find_by_id(self.cpu_id)
  end

  def ram
    Component.find_by_id(self.ram_id)
  end

その後、期待される出力を得ることができます

1.9.3-p194 :002 > Machine.first.cpu.name
  Machine Load (0.2ms)  SELECT "machines".* FROM "machines" LIMIT 1
  Component Load (0.2ms)  SELECT "components".* FROM "components" WHERE "components"."id" = 1 LIMIT 1
  => "CPU1" 

しかし、それは冗長だと感じており、これを行うためのより簡単な方法を探しています。

提案があれば感謝します。

4

2 に答える 2

0

まず、選択ボックスを cpu_id と ram_id で動作させることは正しいことです。そうでない場合、レールは(少し単純化して)行います

machine.cpu = params[:machine][:cpu]

パラメータは常に文字列であるため、これは関連付けに文字列を割り当てようとしているため、コンポーネントを期待しているが文字列を取得しているというエラーが発生します。

2 番目の問題は、関連付けを宣言した方法にあります。ram と cpu の両方の関連付けを設定して、対応する列の ID を格納するために component_id 列を使用しましたが、これは明らかに機能しません。代わりに、CPU アソシエーションで cpu_id 列を使用し、RAM アソシエーションで ram_id 列を使用することをお勧めします。これは実際にはデフォルトです - これらの呼び出しに渡す外部キー オプションを削除すると、問題ありません。

ただし、コンポーネントの関連付けには外部キー オプションが必要です。最も簡単な方法は、コンポーネントが CPU として機能するマシンに 1 つの関連付けを作成し、RAM に別の関連付けを作成することです。単一テーブル継承の設定を検討することもできます。その場合、CPU サブクラスは cpu_id に関連する関連付けのみを持ち、ram サブクラスは ram_id に関連する関連付けのみを持ちます。

于 2013-05-06T18:20:45.407 に答える