Rails:糟糕的关联? [has_many,through]如何测试是否有效?

时间:2016-03-22 12:52:02

标签: ruby-on-rails model associations erd

我正在努力解决数据模型中的问题。我确实有以下型号:

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>

模型说明:

用户可以撰写索赔(索赔属于一个用户),用户可以扮演委员会的角色来执行索赔裁决(最大数量的政委=每项索赔3个)。

有没有办法解决这个问题或改善关系?

2 个答案:

答案 0 :(得分:1)

首先,我建议你回过头来仔细阅读Guide,因为我相信你已经从根本上误解了很多事情。例如,as:选项不表示角色,而是表示存在多态连接。此外,您无法在同一型号上声明has_many :claims两次。无论如何,再去看看吧。

但是,对于你的问题 - 功能虽然有点不优雅的方法可能看起来像:

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个委员会”。

这未经过测试,你可能需要摆弄它才能让它去。但是,希望它会让你朝着更好的方向前进。

祝你好运!

答案 1 :(得分: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将有一堆关系声明我们称之为默认user以外的关系,因此定义了关系的性质。

class_name: 'User'选项告诉ActiveRecord加载类User并使用它来确定要查询的表以及返回结果的类。只要类名不能直接从关联名称派生,就需要它。由于Rails延迟解析类依赖性的方式,该选项应该是一个字符串而不是常量。

现在让我们添加一个政委角色。我们将ruling用作连接表:

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

请注意,为了清楚起见,我们在此处与用户有关系,我们称之为commissioner。现在我们将关系添加到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

请注意source: :claim选项,我们告诉ActiveRecord我们想从联接表中找到哪一方。

当然,为了实现这一点,我们需要正确设置列和外键。这些迁移是从头开始创建表,但您可以轻松地重写它们以更改现有表:

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
  

最大委员数=每项索赔3

这不是关联的一部分,而是通过添加验证或关联回调来强制执行此规则。

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

请参阅: