ruby-on-rails - 我的#build 方法没有保存到数据库
问题描述
我希望我能很好地解释这一点。我正在尝试在我的 rails 应用程序中构建一个表单来创建一个航班,并为其分配一个飞行员。我不断收到“nil:NilClass 的未定义方法‘build’”消息。
这是我的飞行模型
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
我怀疑顺便说一句,这可能与我建立人际关系的方式有关,但很难证明这一点。
当我第一次开始制作它时,这就是我在我的 FlightController 中的#new 方法所拥有的
def new
@flight = Flight.new
@flight.pilot.build
end
但是我最初确实将“@flight.pilot.build”行移到了我的#create方法,因为这是将它保存到数据库的地方,但它仍然给了我相同的“未定义方法`build'为nil: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 %>
我究竟做错了什么?
解决方案
与关联不同,您不能从关联中has_many
创建新实例,has_one/belongs_to
因为当您尚未分配关联时它为 nil。
相反,has_one/belongs_to
类方法生成以下方法:
| | 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
正如您在此处看到的,构建实际上与插入数据库几乎没有关系。相反,它只是填充表单中的字段,如果关联为零,这些字段将不存在。嵌套属性负责实际创建飞行记录。不要提供第二个参数,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
但是,如果用户同时传递了 Pilot_id 和嵌套属性,这可能会导致问题,因此您需要处理这种可能性。
推荐阅读
- python - 如何调试这个 Python 3.7 语法错误?
- swift - 如何在 Swift 的 MKMapView 中将搜索到的位置名称传递给 TextField?
- node.js - Docker 容器中的 NodeJS 14 无法连接到 Postgres DB(输入/输出 docker)
- c - 指向二维数组的 C 指针
- apache-pig - Pig 脚本错误:java.lang.Long 无法转换为 java.lang.Integer
- flutter - Flutter如何制作中圈选择器?
- wordpress - “在您的模板中找不到帖子内容小部件。” (元素)
- three.js - THREE.js - 合并/实例化相同 GLTF 模型的倍数,没有着色器材质..?
- css - CSS Grid - 无法让 grid-template-areas 工作
- python - 使用 matplotlib 绘制多个列表的问题