如何在Rails 5中验证组合值?

问题描述 投票:4回答:1

我正在制作一个医院应用程序,有health_problemspatient_idproblem_type_id组成。

患者可以有多个health_problems,但他们不能是相同的problem_type_id

当我创建一个患者时,我将其health_problems创建为嵌套属性。但验证并没有阻止我为一名患者创建重复的problem_types

这是我的HealthProblem模型:

class HealthProblem < ApplicationRecord
  belongs_to :patient
  belongs_to :problem_type
  validates :problem_type_id, uniqueness: {scope: :patient_id}
end

我也尝试设置problem_type_idpatient_id索引的独特性,但它没有按预期工作(我无法添加任何重复的problem_type /患者,即使有不同的problem_types /患者)


更新:我错误地添加了索引。正如@mmichael所指出的,这是添加索引的正确方法:add_index :health_problems, [:patient_id, :problem_type_id], unique: true

现在数据库抛出了一个错误,阻止它为一个病人添加重复的problem_types。这是一个进步,但仍然不是我需要的。

我需要通过rails验证显示此错误(以某种方式可以正确处理)。


更新2:当使用known rails bug + accepts_nested_attributes_for时,它看起来像validates uniqueness with scope

为多列创建唯一索引将阻止添加重复项,但是 - 直到此版本的Rails(5.1) - 验证将显示两次:首先是sql重复验证,然后是模型具有的其他验证。

由于这只能在数据库级别进行验证,因此不需要模型验证(validates :problem_type_id, uniqueness: {scope: :patient_id})。 Hope Rails团队很快就会修复它。

可以使用rescue_from处理数据库错误。

ruby-on-rails validation ruby-on-rails-5
1个回答
3
投票

我也在Rails 5.1.5上遇到了这个问题。我的解决方案是使用一个独特的数据库索引(如@ artur-haddad建议)与模型around_save回调。我将尝试将我的问题和解决方案解释为简短的大纲。

问题:

Institution (id:integer, name:string)
    | 1
    |
    | *
WaterSupply (id:integer, meter:string, institution_id:integer)

因此,Institution可能有许多water_supplies,每个WaterSupply需要一个独特的meter参考。在我正在使用的常见形式中编辑water_supplies

accepts_nested_attributes_for :water_supplies, reject_if: :all_blank, allow_destroy: true

此时验证

validates :meter, uniqueness: {scope: :institution_id}

由于WaterSupply,在known issue内部不再起作用。

解:

  1. 向表water_supplies添加唯一的数据库索引 add_index :water_supplies, [:meter, :institution_id], unique: true
  2. 添加around_save模型回调 class WaterSupply < ApplicationRecord # Associations belongs_to :institution # Validations validates :institution, :meter, presence: true # Callbacks around_save :catch_uniqueness_exception private def catch_uniqueness_exception yield rescue ActiveRecord::RecordNotUnique self.errors.add(:meter, :taken) end end

就这样。现在,如果属性meter在给定范围内不再是唯一的,则每个保存都会获得一个ActiveModel::Error,并且表单将显示错误already taken

© www.soinside.com 2019 - 2024. All rights reserved.