在保存之前验证必须在另一个模型中运行的Rails表单字段

时间:2021-03-04 11:51:15

I have a Ruby on Rails (Ruby 2.5.1, Rails 5.1) application which deals with flights; among other things, it has a Flight model and an Airline model, with Flight belongs_to :airline and Airline has_many :flights. Airline has an iata_code column which contains the two-letter IATA airline code (i.e. AA, UA, WN, F9).

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

For the flight entry form, I want the user to be able to enter an arbitrary IATA code, rather than selecting an airline from a dropdown. If the IATA code the user enters matches a record in the Airline table, then the Flight.airline_id column should be set to that airline's ID. If the IATA code doesn't match an airline in my database, then the airline should be created using the IATA code provided, and that new airline's ID should be assigned to Flight.airline_id.

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

I currently have this working by having an Airline IATA code field in my add/edit flight form (note that the form field is named :airline_iata instead of :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' %>

Then, in my Flights controller's create and update action, I try to look up the airline before I save the flight, and set the airline_id parameter (create is below, but update is similar):

然后,在我的航班控制器的创建和更新操作中,我尝试在保存航班之前查找航空公司,并设置airline_id参数(创建在下面,但更新类似):

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

This approach is working for me, except that I am unable to figure out how to do validation on the Airline IATA code field from the flight form. The Airline model validates the code (presence: true, length: { is: 2 }, uniqueness: { case_sensitive: false }), so if the user inputs an invalid IATA code, the Airline creation will fail, and thus the Flight creation will fail. However, I won't get the usual validation errors on the Flight form (e.g. error message, form fields with errors highlighted) since Flight's problem is that the airline_id (which isn't directly on the Flight form) isn't present, rather than that airline_iata in the Flight form is invalid.

这种方法对我有用,除了我无法弄清楚如何从航班表格中对航空公司IATA代码字段进行验证。航空公司模型验证代码(在线状态:真实,长度:{是:2},唯一性:{case_sensitive:false}),因此如果用户输入无效的IATA代码,航空公司创建将失败,因此航班创建将失败。但是,我不会在Flight表单上得到通常的验证错误(例如错误消息,突出显示错误的表单字段),因为Flight的问题是airline_id(不直接在Flight表单上)不存在,而是比Flight表格中的airline_iata无效。

How can I set it up so that this form field that has to interact with the Airport model before submission can run the Airport model's validation on a Flight form field, and return form validation errors accordingly?

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

1 个解决方案

#1


1  

You can try to use nested form attributes and add before_validation hook which will override the association:

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

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

In flight.rb you should have

在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

#1


1  

You can try to use nested form attributes and add before_validation hook which will override the association:

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

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

In flight.rb you should have

在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