我正在使用rails 3.0.5,并且我为我的模型进行了自定义验证,当我创建或更新容器记录时,验证有效执行并在必要时添加错误,但记录仍然是保存在数据库中。
我已经测试了自定义验证方法的每一步,并且工作正常。
这是我的模型和自定义验证:
class Container < ActiveRecord::Base
include Comparable
belongs_to :booking
validates :booking, presence: true
validates :kind, presence: true
validates :dimension, presence: true, numericality: { only_integer: true }
validates :retirement_location, presence: true
validates :amount, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 1 }
validate :uniqueness_of_single_fields
scope :group, lambda {|k,d| where('kind = ? and dimension = ?',k,d) }
def self.to_hash
{
"kind" => self.kind.to_s,
"dimension" => self.dimension.to_s,
"retirement_location" => self.retirement_location.to_s
}
end
def uniqueness_of_single_fields
similar_containers = Container.joins(:booking).where('bookings.requirement_id = ? and containers.id != ?', booking.requirement.id, id)
similar_containers = similar_containers.where(assigned_unit: assigned_unit, assigned_seal: assigned_seal)
unless similar_containers.empty?
errors.add(:base, "esa unidad y sello ya han sido asignados")
end
end
def <=>(other)
result = kind <=> other.kind
result == 0 ? dimension <=> other.dimension : result
end
end
P.S。我已经检查了以前的问题,但没有一个能够得到答案
我做了一个rspec测试试试这个
require 'spec_helper'
describe Container do
it 'should have a uniq combination assigned unit and seal for in a requirement' do
requirement1 = Factory(:requirement, reference: 'abc123')
requirement2 = Factory(:requirement, reference: 'qwerty')
booking1 = Factory(:booking, reference: 'FOOBAR123', requirement: requirement1)
booking2 = Factory(:booking, reference: 'FOOBAR456', requirement: requirement1)
booking3 = Factory(:booking, reference: 'FOOBAR789', requirement: requirement2)
container1 = Factory(:container, assigned_unit: 'foo1', assigned_seal: 'bar1', booking: booking1)
container2 = Factory.build(:container, assigned_unit: 'foo1', assigned_seal: 'bar1', booking: booking2)
container3 = Factory(:container, assigned_unit: 'foo1', assigned_seal: 'bar1', booking: booking3)
container1.should be_valid
container3.should be_valid
puts container2.errors
container2.save
puts container2.inspect
container2.should_not be_valid
puts container2.errors.inspect
container2.errors[:base].first.should == "esa unidad y sello ya han sido asignados"
c = Requirement.new(client: 'foo')
c.save
puts c.errors
puts c.inspect
end
end
,输出为:(当它失败时通过)
{}
#<Container id: 6, kind: "dry", dimension: 20, retirement_location: "sitrans", booking_id: 5, created_at: "2011-03-28 22:31:13", updated_at: "2011-03-28 22:31:13", load: "wine 2", assigned_unit: "foo1", assigned_seal: "bar1", temp_celsius: nil, temp_fahrenheit: nil, vent_percentage: nil, vent_cfm: nil, generator: nil, amount: 1>
{:base=>["esa unidad y sello ya han sido asignados"]}
{:service=>["Debe elegir un tipo de servicio"], :shipping_company=>["Debe haber una naviera relacionada con esta orden de servicio"]}
#<Requirement id: nil, client: "foo", service_id: nil, realization: nil, hour: nil, shipping_company_id: nil, description: nil, created_at: nil, updated_at: nil, reference: nil, state: "new">
.
Finished in 0.45293 seconds
1 example, 0 failures
答案 0 :(得分:1)
我认为你很接近,但你一直在阅读片段而不是教程或文档。如果您引用Railscast Episode On Rails3 Validations,那么您将看到他们建议您定义自己的自定义验证器子类,而不是将另一个函数粘贴到模型上。
你可能会说抽象不仅仅是因为它“更合适”还是其他一些?然后查看API doc for Validations
在这里,他们说“最小的实现”就像:
class Person
include ActiveModel::Validations
attr_accessor :first_name, :last_name
validates_each :first_name, :last_name do |record, attr, value|
record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
end
end
或
class Person
include ActiveModel::Validations
validates :instance_validations
def instance_validations
validates_with MyValidator
end
end
最重要的是API entry for the validates
method,他们甚至建议您在类/模型中定义自定义的Validator类:
class Film
include ActiveModel::Validations
class TitleValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors[attribute] << "must start with 'the'" unless value =~ /\Athe/i
end
end
validates :name, :title => true
end
干杯!