首页 > 解决方案 > 在保存之前验证必须通过另一个模型运行的 Rails 表单字段

问题描述

我有一个处理航班的 Ruby on Rails(Ruby 2.5.1,Rails 5.1)应用程序;除其他外,它有一个Flight模型和一个Airline模型,带有Flight belongs_to :airlineAirline has_many :flightsAirline有一iata_code列包含两个字母的 IATA 航空公司代码(即AA, UA, WN, F9)。

对于航班输入表单,我希望用户能够输入任意 IATA 代码,而不是从下拉列表中选择航空公司。如果用户输入的 IATA 代码与Airline表中的记录匹配,则该Flight.airline_id列应设置为该航空公司的 ID。如果 IATA 代码与我数据库中的航空公司匹配,则应使用提供的 IATA 代码创建航空公司,并将新航空公司的 ID 分配给Flight.airline_id.

我目前通过在我的添加/编辑航班表单中有一个航空公司 IATA 代码字段来实现此功能(请注意,表单字段被命名为:airline_iata而不是:airline_id):

<%= f.label :airline_iata, "Airline code" %>
<%= f.text_field :airline_iata, class: "form-control code airline-iata", maxlength: 2, placeholder: 'AA' %>

然后,在我的航班控制器createupdate操作中,我尝试在保存航班之前查找航空公司,并设置airline_id参数(create如下所示,但update类似):

def create
  params[:flight][:airline_id] = Airline.find_or_create_by!(:iata_code => params[:flight][:airline_iata].upcase).id
  @flight = current_traveler.flights.build(flight_params)

  if @flight.save
    redirect_to event_path(current_traveler.event)
  else
    render "new"
  end

end

这种方法对我有用,除了我无法弄清楚如何从航班表格中对航空公司 IATA 代码字段进行验证。Airline 模型会验证代码 ( presence: true, length: { is: 2 }, uniqueness: { case_sensitive: false }),因此如果用户输入了无效的 IATA 代码,则 Airline 创建将失败,因此 Flight 创建将失败。但是,我不会在 Flight 表单上看到通常的验证错误(例如错误消息、突出显示错误的表单字段),因为 Flight 的问题是airline_id(不直接在 Flight 表单上)不存在,而不是航班表格中airline_iata的内容无效。

如何设置它,以便在提交之前必须与 Airport 模型交互的表单字段可以在 Flight 表单字段上运行 Airport 模型的验证,并相应地返回表单验证错误?

标签: ruby-on-rails

解决方案


您可以尝试使用嵌套表单属性并添加 before_validation 钩子,它将覆盖关联:

<%= f.fields_for :airline do |airline_form| %>
  <%= airline_form.label :iata_code, "Airline code" %>
  <%= airline_form.text_field :iata_code %>
<% end %>

在 flight.rb 你应该有

accept_nested_attributes_for :airline
before_validation :check_existing_airline, on: :create

def check_existing_airline
  if airline.new_record? && existing_airline = Airline.find_by(iata_code: airline.iata_code)
    self.airline = exisitng_airline
  end
end

推荐阅读