2016-03-22 5 views
3

私は自分のデータモデルの問題に苦労しています。Rails:Bad Associations? [has_many、through]動作しているかどうかテストするには?

class User < ActiveRecord::Base 
... 
    has_many :claims #user-claims 
    has_many :claims, through: :rulings, as: :commissars 
... 
end 

class Claim < ActiveRecord::Base 
... 
    belongs_to :user 
    has_many :users, through: :rulings, as: :commissars 
... 
end 

class Ruling < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :claim 
end 

エラー:

undefined method `commissars' for #<Claim:0xc5ac090> 

モデルの説明:

ユーザーがクレームを(請求項は、1人のユーザーに属する)書き込むことができ、ユーザーがの役割を行うことができ、私は次のモデルを持っていますクレームの判決を行うためのコミッショナー(クレームごとにコミッショナーの最大数= 3)。

これを修正したり関係を改善する方法はありますか?

+1

'Ruling'モデルを提示してください。 – jvillian

+0

'クラスの判決 Nikos4Life

+0

、感謝 – Nikos4Life

答えて

1

このドメインモデルは非常に複雑な関係を必要とするので、最初の試行でそれを得られないということは恥ずかしいことではありません。

class User < ActiveRecord::Base 
    has_many :claims, foreign_key: 'claimant_id', 
        inverse_of: :claimant 
end 

class Claim < ActiveRecord::Base 
    belongs_to :claimant, class_name: 'User', 
         inverse_of: :claims 
end 

これは、ひねりを加えた多くの関係にかなり基本的なものである:

は、ユーザーおよび特許請求の範囲で始めることができます。ユーザーは主張との関係がたくさんあるので、関係の性質が定義されるように、デフォルトのuser以外の関係を呼び出します。

class_name: 'User'オプションは、ActiveRecordにクラスUserをロードし、それを使用して照会するテーブルと結果を返すクラスを特定します。クラス名が連想の名前から直接派生することができないときはいつでも必要です。このオプションは文字列でなければならず、Railsがクラスの依存関係を遅延解決するために定数でなければなりません。

ここでコミッショナルロールを追加できます。私たちは、参加するテーブルとしてrulingを使用します。ここでは、我々は明確にするためcommissionerを呼び出して、ユーザーとの関係を持っていることを

class Ruling < ActiveRecord::Base 
    belongs_to :claim 
    belongs_to :commissioner, class_name: 'User' 
end 

注意してください。今、私たちはClaimに関係を追加します。

class Claim < ActiveRecord::Base 
    belongs_to :claimant, class_name: 'User', 
         inverse_of: :claims 
    has_many :rulings 
    has_many :commissioners, through: :rulings 
end 

その後、我々は、ユーザー側の関係をセットアップする必要があります。

class User < ActiveRecord::Base 
    has_many :claims, foreign_key: 'claimant_id', 
        inverse_of: :claimant 

    # rulings as claimant 
    has_many :rulings, through: :claims 

    has_many :rulings_as_commissioner, class_name: 'Ruling', 
            foreign_key: 'commissioner_id' 
    has_many :claims_as_commissioner, through: :rulings_as_commissioner, 
            source: :claim 
end 

は、我々は我々が参加テーブルから対象となるパーティのActiveRecordを伝えるsource: :claimオプションに注意してください。 。

もちろん、これを行うには、列と外部キーを適切に設定する必要があります。これらの移行は最初からテーブルを作成することですが、簡単にあなたの既存のテーブルを変更するためにそれらを書き換えることができます。これは本当に団体の一部ではない、むしろあなたが検証を追加することで、この規則を施行う

class CreateClaims < ActiveRecord::Migration 
    def change 
    create_table :claims do |t| 
     t.belongs_to :claimant, index: true, foreign_key: false 
     t.timestamps null: false 
    end 
    # we need to setup the fkey ourself since it is not conventional 
    add_foreign_key :claims, :users, column: :claimant_id 
    end 
end 

class CreateRulings < ActiveRecord::Migration 
    def change 
    create_table :rulings do |t| 
     t.belongs_to :claim, index: true, foreign_key: true 
     t.belongs_to :commissioner, index: true, foreign_key: false 
     t.timestamps null: false 
    end 

    add_foreign_key :rulings, :users, column: :commissioner_id 
    add_index :rulings, [:claim_id, :commissioner_id], unique: true 
    end 
end 

max numbers of commissars = 3 per claim

かアソシエーションコールバック

class Ruling < ActiveRecord::Base 

    # ... 

    validate :only_three_rulings_per_claim 

    private 

    def only_three_rulings_per_claim 
     if claim.rulings.size >= 3 
     errors.add(:claim, "already has the max number of commissars") 
     end 
    end 
end 

参照:

+0

デモはhttps://github.com/maxcal/playground/tree/36157065から入手可能 – max

+0

完全に存在するので、 'claim.rulings.size == 3 'ではなく' claim.rulings.size> = 3'を使用します3つ以上が競争条件のために突き抜ける可能性があります! – max

+1

素晴らしいソリューション、最大。私はあなたを知らないが、私はいつもあなたの答えを見ることで多くを学ぶ。 SOを大事にしてくれてありがとう。 – jvillian

1

最初に、あなたが元に戻って、Guideをよく読んで、あなたが根本的に多くのことを誤解していると思うことをお勧めします。たとえば、as:オプションは役割を示すのではなく、多態的な結合の存在を示します。また、同じモデルでhas_many :claimsを2回宣言することはできません。とにかく、もう一度読んでください。

しかし、あなたの質問に - やや無粋なアプローチがどのように見えるかもしれませんが、機能:

class User < ActiveRecord::Base 
    ... 
    has_many :claims 
    has_many :claim_commissars, foreign_key: "commissar_id" 
    has_many :commissar_claims, through: :claim_commissars, class_name: "Claim" 
    #              ^^^^^^^^^^^^^^^^^^^^^ 
    #              this bit may be wrong 
    ... 
end 

class Claim < ActiveRecord::Base 
    ... 
    belongs_to :user 
    has_one :ruling 
    has_many :claim_commissars 
    has_many :commissars, through: :claim_commissars 
    ... 
end 

class ClaimCommissar < ActiveRecord::Base 
    ... 
    belongs_to :claim 
    belongs_to :commissar, class_name: "User" 
    ... 
end 

class Ruling < ActiveRecord::Base 
    ... 
    belongs_to :claim 
    belongs_to :commissar, class_name: "User" 
    ... 
end 

あなたはコードの中で、あなたの「最大3 commissars`を施行する必要があります。

これはテストされていないため、実際に試してみる必要があります。しかし、うまくいけば、あなたをより良い方向に向けることができます。

幸運を祈る!

+0

はありがとう、私は努力のためにできるだけ早く感謝をテストします最初のコメントを更新:) – Nikos4Life

+0

そのA素敵な作業ですが、 'Ruling'はすでにユーザーとクレームの間のm2m結合テーブルとして機能しているので、' ClaimCommissar'テーブルは必要ありません。 – max

+0

私はそれに同意します。私は、裁定(これは私の仮定 - おそらく間違っている)は「賠償請求」が「裁判」と関連しているので、裁定が結合モデルとして使用されることが奇妙に思えると思っていた(ライターとして)、Commissarとしての1つ以上の 'User'sと組み合わせて使用​​します。したがって、新しいモデルの導入。しかし、それはすべてドメインについての前提に基づいています。 – jvillian

関連する問題