噛むのにとても良い問題。
最初に、あるモデルに関する何かを別のモデルに公開する必要があります。特定の種類の役割 (調査責任者) があり、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