使用范围来跟踪新记录验证

时间:2014-02-09 18:30:08

标签: ruby-on-rails validation ruby-on-rails-4 rails-activerecord has-many

假设我ProjectsPeople属于ProjectPerson可以是领导者,也可以不是领导者,并具有此范围。 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方式。

有更好的方法吗?

2 个答案:

答案 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关系。