1

Project と User という 2 つのオブジェクトがあり、ProjectAssignment というオブジェクトによって結合されています。ProjectAssignments オブジェクトには、追加のフィールド project_role があります。以下にモデルを示します。

class Project < ActiveRecord::Base
  #relationships
  has_many :project_assignments
  has_many :users, :through => :project_assignments
end

class ProjectAssignment < ActiveRecord::Base
  belongs_to :project
  belongs_to :user
  belongs_to :project_role
end

class User
  has_many :project_assignments
  has_many :projects, :through => :project_assignments
end

特定のプロジェクトについて、project_role が「Principal Investigator」である ProjectAssignment が常に 1 つだけ存在することを検証する必要があります。ProjectAssignment モデルで検証を記述する方法が少しわかりません。最初に現在の PI の設定を解除すると、プリンシパル調査員は 1 人未満になり、ユーザーをプリンシパル調査員に設定してから他のユーザーを設定解除すると、1 人を超えます。

class ProjectAssignment
  validates :allow_exactly_one_pi

  def require_exactly_one_pi  
    if self.project_role.name == 'Principal Investigator' and other_princ_inv_exists
      #more than one principle investigator set => error
    elseif was_principle_investigator
      #no principle investigator set => error
    end
  end
end

これをどのように処理すべきか提案はありますか?

4

2 に答える 2

0

要約形式で、完全に異なるアプローチを次に示します。

1) 新しいプロジェクトを作成するときは、PI になるべきユーザーを尋ね、最初の ProjectAssignment レコードを作成します。これにより、少なくとも 1 つの PI があることを検証する必要がなくなります。

2) ProjectController とプロジェクト編集ビューで、ある種の「PI の変更」インターフェイスを作成します。そのインターフェイスでは、新しい PI になるユーザーの user_id と、現在の PI が必要とする project_role_id を知る必要があります。 project_role_id が nil の場合、ユーザーをプロジェクトから削除する必要があることを意味します。

このアプローチは、検証を完全に排除します!

最初の回答は非常に楽しい演習でした。アプローチ全体を変更するよう誰かに提案するのは嫌いです。むしろ、質問された質問にできる限り答えたいと思います。しかし、「検証」がどれほど複雑になったかを見た後、あなたのケースではそれらに依存しないのが最善だと思います。条件が満たされていることを確認するには、アプリをコード化する必要があります。

HTH

于 2012-04-17T16:42:36.793 に答える
0

噛むのにとても良い問題。

最初に、あるモデルに関する何かを別のモデルに公開する必要があります。特定の種類の役割 (調査責任者) があり、ProjectAssignment はその「特別なケース」の役割を認識している必要があります。しかし!その特別なステータスを追跡するのはプロジェクト ロールである必要があるため、ProjectRole モデルにメソッドを追加します。

ProjectRole < ActiveRecord::Base
  def ispi?
    self.name == 'Principal Investigator'
  end
end

次に、すべての project_asssignments を反復処理し、それらのいずれかが主任調査員であるかどうかを判断する方法を理解する必要があります。project_assignment のインスタンスから Class メソッドにアクセスする必要があります。

class ProjectAssignment < ActiveRecord::Base
  validate :there_can_only_be_one_principal_investigator

  def there_can_only_be_one_principal_investigator
    error = false
    self.class.where('project_id = ?',self.project_id).each do |p|
      if p.project_role.ispi?
         error = true
         break
      end
    end
    if error
      #whatever
    end
  end
end

ここで、関連付けを変更する必要があります。ProjectAssignment は 1 つのロールしか持てないため、

class ProjectAssignment < ActiveRecord::Base
  belongs_to :project
  belongs_to :user
  has_one :project_role
end

そのため、すでにそのプロジェクトに主任研究者が存在する場合、project_role == 主任研究者で ProjectAssignment を追加することはできません。

更新についてはどうですか。project_role == PI で ProjectAssignment を更新し、PI で別の ProjectAssignment が既に存在する場合、検証はそれをキャッチします。

少なくとも 1 つの PI があることをどのように保証しますか? これが意味することは、1 つのプロジェクトの最初の ProjectAssignment が PI でなければならないということだと思います。これは、少しハックする必要がある場所です。ProjectRole モデルに関する知識を ProjectAssignment モデルで直接公開する必要があります。

validate  :there_must_be_at_least_one_principal_investigator

def there_must_be_at_least_one_principal_investigator
  if self.class.where('project_id = ?', self.project_id).count() == 0 AND !self.project_role_id == 1
     #error
  end
end

PI ロールの ID が 1 (またはそれが何であれ) であるという事実は、別のモデルでハードコーディングされているため、このソリューションはあまり好きではありません!! 少し不快にならないようにするために、 Class メソッドを ProjectRole モデルに追加できます

class ProjectRole < ActiveRecord::Base
  def self.piid
     1 # or whatever it is
  end
end

次に、これを行います:

def there_must_be_at_least_one_principal_investigator
  if self.class.where('project_id = ?', self.project_id).count() == 0 AND ! self.project_role_id == ProjectRole.piid
     #error
  end
end

では、どのように主任研究者を変更しますか? 別のアクションでそれを行う必要があります。つまり、

class ProjectAssignmentController < ApplicationController
  def change_pi
     @proj_assignment1 = ProjectAssignment.find(params[:orig_pi_id])
     @proj_assignment2 = ProjectAssignment.find(params[:new_pi_id])
     @proj_assignment1.project_role_id = params[:new_role_for_orig_pi].to_i
     @proj_assignment1.save :validate=>false # it's OK,  you're taking care of it below
     @proj_assignment2.project_role_id = ProjectRole.piid
     @proj_assignment2.save
  end
end
于 2012-04-16T23:31:34.510 に答える