我的#build方法未保存到数据库

时间:2020-08-02 06:06:05

标签: ruby-on-rails

我希望我能解释清楚。我正在尝试在Rails应用程序中构建表单以创建航班,并为其分配一名飞行员。我不断收到“未定义的方法'build'for nil:NilClass”消息。

这是我的航班型号

class Flight < ApplicationRecord
    belongs_to :pilot
    belongs_to :passenger
    accepts_nested_attributes_for :pilot

end

这是我的飞行员模型

class Pilot < ApplicationRecord
    has_many :flights
    has_many :pilots, through: :flights
    
    
end

我怀疑顺便说一句,这可能与我建立人际关系的方式有关,但很难证明这一点。

这是我第一次开始在我的FlightsController中拥有#new方法的方法

def new
        @flight = Flight.new
        @flight.pilot.build
end

但是我确实确实将“ @ flight.pilot.build”行移到了我的#create方法中,因为那是将其保存到数据库中的地方,但是它仍然为我提供了相同的“未定义方法`build” :NilClass“

def create
        @flight = Flight.create(flight_params)
        @flight.pilot.build
        if @flight.save
            redirect_to flight_path(@flight)
        else
            render 'new'
        end
    end


private

    def flight_params
        params.require(:flight).permit(:flight_number, :destination, pilot_attributes: [:name, :rank])
    end

这是我要在其上执行创建飞行任务的视图。

<h1>Create New Flight</h1>

<%= form_for(@flight) do |f|%>
<div>
<%= f.label :flight_number %>
<%= f.text_field :flight_number %>
</div><br>

<div>
<%= f.label :destination %>
<%= f.text_field :destination %>
</div><br>

<div>
<label for="flight_pilot_id">Assign a pilot:</label>
<%= f.collection_select :pilot_id, Pilot.all, :id, :name, {:prompt => "Select a Pilot"} %>
</div><br>


    <h3>Or Create new Pilot:</h3>
    <%= f.fields_for :pilot, Pilot.new do |pilot_attributes|%>
        <%= pilot_attributes.label :name, "Pilot name:" %>
        <%= pilot_attributes.text_field :name %>
        <br>
        <%= pilot_attributes.label :rank, "Rank:" %>
        <%= pilot_attributes.text_field :rank %>
        <br>
    <% end %>



<%= f.submit %>

<% end %>

我在做什么错了?

2 个答案:

答案 0 :(得分:2)

has_many关联不同,您不能从has_one/belongs_to关联中创建新实例,因为当您尚未分配关联时,它为nil。

has_one/belongs_to class methods会生成以下方法:

                                  |            |  belongs_to  |
generated methods                 | belongs_to | :polymorphic | has_one
----------------------------------+------------+--------------+---------
other                             |     X      |      X       |    X
other=(other)                     |     X      |      X       |    X
build_other(attributes={})        |     X      |              |    X
create_other(attributes={})       |     X      |              |    X
create_other!(attributes={})      |     X      |              |    X
reload_other                      |     X      |      X       |    X

您的控制器应实际读取:

def new
  @flight = Flight.new(flight_params)
  # this just seeds the inputs
  @flight.build_pilot 
end

def create
  @flight = Flight.new(flight_params)
  if @flight.save
    redirect_to @flight
  else
    # this just seeds the inputs
    @flight.build_pilot unless @flight.pilot 
    render :new
  end
end

正如您在这里看到的那样,构建实际上与插入数据库几乎没有关系。相反,它只是以关联为nil时不会显示的形式填充字段。嵌套属性负责实际创建Flight记录。不要为fields_for提供第二个参数,因为它没有提供默认值-它会覆盖那里的任何现有记录。

<%= f.fields_for :pilot do |pilot_attributes| %>
  <%= pilot_attributes.label :name, "Pilot name:" %>
  <%= pilot_attributes.text_field :name %>
  <br>
  <%= pilot_attributes.label :rank, "Rank:" %>
  <%= pilot_attributes.text_field :rank %>
  <br>
<% end %>

如果您要分配现有记录,只需传递pilot_id参数并将其列入白名单:

<%= form_with(model: @flight) do |f| %>
  <%= f.collection_select(:pilot_id, Pilot.all, :name, :id) %>
<% end %>
def flight_params
  # be nice to maintainers and don't write your strong parameters 
  # in one mega-line
  params.require(:flight)
        .permit(
          :flight_number, :destination, :pilot_id,
          pilot_attributes: [:name, :rank]
        )
end

但是,如果用户同时传递了领航ID和嵌套属性,则可能会导致问题,因此您需要处理这种情况。

答案 1 :(得分:-1)

belongs_to关联返回相关对象或nil。 在您的情况下,您将嵌套属性用于pilot。您需要删除@flight.pilot.build,然后在致电flight_params时从Flight.create(flight_params)创建一个飞行员。它可能取决于您的Rails版本,您需要检查模型Flight的设置以拥有accepts_nested_attributes_for :pilot

此外,我相信您需要在pilot_id中拥有flight_params才能分配现有飞行员。

params.require(:flight).permit(:flight_number, :destination, :pilot_id, pilot_attributes: [:name, :rank])