我希望我能解释清楚。我正在尝试在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 %>
我在做什么错了?
答案 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])