Rails 条件验证:if: 不起作用



我是rails的新手,我有一个带有三个外键的trip类。其中两个与同一个类关联:Place.

这是我的模型:

class Trip < ApplicationRecord
belongs_to :from, class_name: "Place", foreign_key: "from_id"
belongs_to :to, class_name: "Place", foreign_key: "to_id"
belongs_to :vehicle, class_name: "Vehicle", foreign_key: "vehicle_id"
validates :price, presence: true
validates :time, presence: true
validates :from_id, presence: true
validates :to_id, presence: true, if: :from_different_to?

def from_different_to?
to_id != from_id
end
end

除最后一个外,所有模型测试都通过:

class TripTest < ActiveSupport::TestCase
def setup
@place1 = Place.create(name:"NewYork",cap:"11111",lat:"1234",long:"1478")
@place2 = Place.create(name:"Los Angeles", cap:"22222", lat:"1234",long:"1478")
@vehicle = Vehicle.create(targa: "ab123cd",modello:"500",marca:"Fiat", posti:5,alimentazione:"benzina")
@trip = Trip.new(price: 10, time: Time.new(2021, 10, 14, 12,03), from_id: @place1.id, to_id: @place2.id,vehicle_id: @vehicle.id)  
end
...
test "Departure id and arrival id should be different" do
@trip.to_id = @place1.id
assert_not @trip.valid?
end

导致失败:

Failure:
TripTest#test_Departure_id_and_arrival_id_should_be_different [/media/alessandro/DATA/Universita/Magistrale/1_anno/Programmazione_concorrente/hitchhiker/test/models/trip_test.rb:45]:
Expected true to be nil or false

我不明白为什么。有人能帮帮我吗?

似乎你认为validates ... if:的工作方式与实际情况不同。这条线

validates :to_id, presence: true, if: :from_different_to?
如果from_different_to方法返回true

,则转换为验证to_id是否存在。当from_different_to求值为false时,不验证。

这意味着当你定义

@trip.to_id = @place1.id
assert_not @trip.valid?

在您的测试中,那么第一行禁用检查to_id的存在。没有验证,没有错误…

我想你真正想要实现的是验证to_id存在from_idto_id不相等。这可以通过像这样的自定义验证来完成:

validates :to_id, presence: true
validate :validates_places_are_different
private
def validates_places_are_different
errors.add(:to_id, "must be different to from_id") if to_id == from_id
end

我不明白为什么。有人能帮帮我吗?

if有条件地启用验证。您的to_idfrom_id相同,因此to_id根本没有验证。但即使是,to_id也有一个值,所以这个字段不会出现错误。


总的来说,我不太确定为什么你在这里期望验证错误或错误应该是什么。根据我的经验,像assert_not @model.valid?这样的断言实际上是无用的。由于不相关的原因,记录可能无效,而您不知道。就我个人而言,我断言的正是我所期望的错误消息。以下内容(rspec语法)

it "requires first_name" do
expected_messages = {
first_name: [:blank],
}
@model.valid?
expect(@model.errors.full_messages).to eq expected_messages
end

@spickermann的替代方法是:

class Trip < ApplicationRecord
belongs_to :from, class_name: "Place", foreign_key: "from_id"
belongs_to :to, class_name: "Place", foreign_key: "to_id"
belongs_to :vehicle, class_name: "Vehicle", foreign_key: "vehicle_id"
validates :price, presence: true
validates :time, presence: true
validates :from_id, presence: true
validates :to_id, numericality: {other_than: :from_id}, if: :from_place_id?
def from_place_id
from_id
end
def from_place_id?
!from_id.nil?
end
end

请注意,我们必须放置一个控件来执行最后的验证,只有当from_id不是null时,因为如果我们不这样做,我们就会在上级行上删除控件validates :from_id, presence:true