Rails通过一对多关系进行条件验证

时间:2015-09-27 07:11:22

标签: ruby-on-rails

我正在尝试对字段进行条件验证。这样它只验证另一个字段是否是特定值。这里的问题是,这个其他领域是一对多的关系,我似乎无法让它工作。 以下是相关代码:

class CreateInvolvedPartyTypes < ActiveRecord::Migration
  def change
    create_table :involved_party_types do |t|
      t.string :code
      t.string :name

      t.timestamps null: false
    end
  end
end

class CreateInvolvedParties < ActiveRecord::Migration
  def change
    create_table :involved_parties do |t|
      t.string :first_name
      t.string :last_name
      t.references :involved_party_type

      t.timestamps null: false
    end
  end
end

class InvolvedParty < ActiveRecord::Base
    def ipt_cd?
        self.involved_party_type.code == 'I'
    end

    validates :first_name, presence: { message: "Please insert first name" }
    validates :last_name, presence: { message: "Please insert last name" }, :if => :ipt_cd?
    validates :involved_party_type, presence: { message: "Please select involved party type" }

    belongs_to :involved_party_type
end

以上代码失败:

  

nil的未定义方法`code':NilClass

感谢您的帮助

2 个答案:

答案 0 :(得分:1)

错误意味着InvolvedParty中的self.involved_party_type#ipt_cd?没有。您应该在调用#code之前测试involved_pa​​rty_type的存在,或者使用#try。

def ipt_cd?
  return false if involved_party.nil?
  involved_party_type.code == 'I'
end

def ipt_cd?
  self.involved_party_type.try(:code) == 'I'
end

或者,如果related_pa​​rty_type存在,则只能通过调用验证来避免此问题。

validates :last_name, presence: { message: "Please insert last name" }, if: -> { involved_party_type && ipt_cd? }

答案 1 :(得分:0)

我认为问题在于您对调用instanceclass级别数据感到困惑。

  
      
  • &#34;实例&#34;每次调用类时都会填充数据
  •   
  • &#34;级&#34;数据是静态的,总是附加到类
  •   

两者之间的结构差异是class数据通常通过 self (EG def self.method&amp; self.attribute)调用,而instance使用&#34; naked&#34;调用数据属性(IE没有self)。

您正在调用以下内容:

def ipt_cd?
   self.involved_party_type.code == 'I'
end

问题是您引用self就好像它是数据一样。你想要的是实例等价物:

def ipt_cd?
   involved_party_type.code == 'I'
end

正如另一个答案所述,您的错误是由一段没有code方法的数据引起的,这意味着它没有。

这是casue(解决方案在上面 - IE删除self):

involved_party_type.code == 'I'

因此,如果您想确保没有收到此错误,则必须确保involved_party_type存在。这可以通过首先确保您引用数据的实例变体,然后确保它在那里完成。另一个答案提供了实现这一目标的最佳方法。

最后,我认为你的结构可以改进。

在我看来,引用相关字段的实际数据表示是不好的做法。您是否正在尝试创建新数据,但您是否引用了相关属性?

为什么不这样做:

#app/models/party_type.rb
class PartyType < ActiveRecord::Base
   has_many :involved_parties
end

class InvolvedParty < ActiveRecord::Base
   belongs_to :party_type

   validates :first_name, :party_type, presence: true
   validates :last_name, presence: { message: "Please insert last name" }, if: :cd?

   private

   def cd?
      party_type == PartyType.find_by(code: "I").pluck(:id)
   end
end

这将发送另一个数据库查询它会删除对特定数据的依赖性。您当前的设置不依赖于外键,而是依赖于可能更改的值。

虽然此建议还依赖于数据(IE code == I),但它会将其用作ActiveRecord中的量词。也就是说,您不是在比较数据,而是在比较关系。