假设我Projects
和People
属于Project
。 Person
可以是领导者,也可以不是领导者,并具有此范围。 Project
必须至少有一个领导者,否则无效。所以我尝试了这个:
class Project < ActiveRecord::Base
has_many :people
validate :has_a_leader
def has_a_leader
unless self.people.lead.size > 0
puts 'Must have at least one leader'
errors.add(:people, 'Must have at least one leader')
end
end
end
class Person < ActiveRecord::Base
belongs_to :project
scope :lead, -> { where(:is_lead => true) }
end
不幸的是,验证仅适用于已保存的记录,因为新记录的范围始终为空:
p = Project.new
p.people.build(:is_lead => true)
=> #<Person ..., is_lead: true>
p.people
=> #<ActiveRecord::AssociationRelation [#<Person ..., is_lead: true>]>
p.people.lead
=> #<ActiveRecord::AssociationRelation []>
p.valid?
'Must have at least one leader'
=> false
使用其他语法进行另一次尝试:
p = Project.new
p.people.lead.build
=> #<Person ..., is_lead: true>
p.people.lead
=> #<ActiveRecord::AssociationRelation []>
p.people
=> #<ActiveRecord::AssociationRelation []> # <-- first syntax at least got something here
p.valid?
'Must have at least one leader'
=> false
所以看起来我必须像这样重写验证并在创建新项目时使用第一种语法:
def has_a_leader
unless self.people.find_all(&:is_lead).size > 0
puts 'Must have at least one leader'
errors.add(:people, 'Must have at least one leader')
end
end
但现在我有两个地方,我已经定义了一个领导者:在验证方法和范围lambda中。我重复一遍。有效,但不是Rails方式。
有更好的方法吗?
答案 0 :(得分:2)
您可以通过添加其他关联来解决您的问题:
class Project < ActiveRecord::Base
has_one :leader, -> { where(is_lead: true) }, class_name: 'Person'
validates :leader, presence: true
end
创建Project
时,您可以轻松设置潜在客户:
def create
project = Project.new(params[:project])
project.leader.new(name: 'Corey') #=> uses the scope to set `is_lead` to `true`
end
您的lead
模型中仍然有Person
范围重复,但由于已经定义了,我们只需使用它:
class Project < ActiveRecord::Base
has_one :leader, Person.method(:lead), class_name: 'Person'
end
这样做的好处就是让抓住项目负责人变得容易多了。
答案 1 :(得分:0)
您是否考虑过在项目表中添加leader_id或main_leader_id?我知道您的项目可能有多个领导者,但实施的一个潜在问题是:假设您创建了一个项目,并且在一个人身上分配了谁是领导者,那么它是有效的 - 很棒。之后,该人员将从项目中删除(通过更改Person的project_id属性)。除非你对Person进行回调,否则你的Project不会知道它不再有一个领导者,而且它将处于无效状态。如果您有其他代码假定Project有效且至少有一个领导者(即my_project.leaders.first.do_something),那么这可能会导致问题。如果你有类似main_leader_id的东西,那么你可以在Project模型中简单地验证它(使用presence:true),如果你需要获得所有领导者,你仍然可以使用has_many关系。