【问题标题】:How to validate combined values in Rails 5?如何验证 Rails 5 中的组合值?
【发布时间】:2018-02-14 04:04:01
【问题描述】:

我正在制作一个医院应用程序,其中有 health_problems,它们由 patient_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 索引设置为唯一,但没有按预期工作(即使使用不同的问题类型/患者,我也无法添加任何重复的问题类型/患者)


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

现在数据库向我抛出一个错误,阻止它为一名患者添加重复的问题类型。这是一个进步,但仍然不是我需要的。

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


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

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

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

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

【问题讨论】:

  • 当您说您尝试添加唯一索引时,您是否尝试过add_index :health_problems, [:patient_id, :problem_type_id], unique: true
  • @mmichael 实际上我正在添加单独的索引(这是一个错误)。你说的方式如我所愿,但它只在数据库中验证,它返回:“Mysql2::Error: Duplicate entry '6-3' for key 'index_health_problems_on_patient_id_and_problem_type_id'”我需要它来返回一个验证错误。
  • 假设您在创建 HealthProblem 记录时使用 accepts_nested_attributes_for,有一个带有各种解决方法的 known rails issue。我建议您从创建记录的任何地方从ActiveRecord::RecordNotUnique 中解救出来。如果您在控制器中执行此操作,请查看 rescue_from,或者您可以简单地将您的记录创建块包装在标准 rescue 中。
  • 没错,我正在使用accepts_nested_attributes_for,它看起来像你指出的一个rails错误。对您的回答有很大帮助,谢谢。
  • 不客气

标签: ruby-on-rails validation ruby-on-rails-5


【解决方案1】:

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

问题:

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

【讨论】:

  • 当我使用这个方法时,我得到'ActiveRecord::RecordNotSaved: Failed to save the record'
  • 当我使用它并尝试创建具有重复条目的两个嵌套记录的父模型时,未保存的父模型仍然有一个 id 和 #new_record 之类的持久性方法?和#persisted?不正确。
猜你喜欢
  • 2011-02-01
  • 2013-11-23
  • 1970-01-01
  • 1970-01-01
  • 2011-11-17
  • 1970-01-01
  • 2015-05-09
  • 2012-01-30
  • 1970-01-01
相关资源
最近更新 更多