我有两个对象,Project和User,它们由一个名为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
我必须验证对于给定项目,任何时候都只有一个ProjectAssignment,其project_role为“Principal Investigator”。我有点不确定如何在ProjectAssignment模型中编写验证。如果我首先取消当前的PI,那么主要调查员少于1个,如果在取消设置另一个之前将用户设置为Principal Investigator,则超过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
有关如何处理此事的任何建议吗?
答案 0 :(得分: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只能有一个角色,所以
class ProjectAssignment < ActiveRecord::Base
belongs_to :project
belongs_to :user
has_one :project_role
end
现在您可以放心,如果该项目已存在Principal Investigator,则无法使用project_role == Principal Investigator添加ProjectAssignment。
如果更新,您使用project_role == PI更新ProjectAssignment,并且已经有另一个带有PI的ProjectAssignment,验证将捕获该更新。
现在,您如何确保至少有一个PI?我认为这意味着任何一个项目的FIRST ProjectAssignment都必须是PI。这是你必须有点hackish的地方,你必须直接在ProjectAssignment模型中公开有关ProjectRole模型的知识。
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(或其他任何东西)的事实在另一个模型中被硬编码!为了使它有点令人反感,你可以在ProjectRole模型中添加一个Class方法
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
答案 1 :(得分:0)
这是一种完全不同的方法,摘要格式为:
1)当您创建一个新项目时,请询问应该是PI的用户,并构建第一个ProjectAssignment记录,这样就无需验证是否至少有一个PI。
2)在ProjectController和项目编辑视图中,创建某种“更改PI”界面,在该界面中,您需要知道成为新PI的用户的user_id,以及project_role_id,如果他留在项目上,如果project_role_id为nil,则需要分配当前PI,这意味着应该从项目中删除用户。
这种方法完全取消了验证!
第一个答案是一个非常有趣的练习,我讨厌建议有人改变他们的整个方法,我宁愿回答问题,尽我所能。但在看到“验证”变得多么复杂之后,我认为最好不要在你的情况下依赖它们,你只需要代码你的应用程序以确保你的条件得到满足。
HTH