Ruby on rails 在另一个模型中为具有嵌套属性的模型渲染部分
我有一个rails应用程序,可以为房子建模<代码>房屋包含Ruby on rails 在另一个模型中为具有嵌套属性的模型渲染部分,ruby-on-rails,partial,renderpartial,Ruby On Rails,Partial,Renderpartial,我有一个rails应用程序,可以为房子建模房屋包含房间,房间具有灯光和小型家电的嵌套属性。我有一个计算器控制器,这是最终用户访问应用程序的方式 我的问题是,我无法从计算器获取添加房间以正确渲染和提交的部分。初始页面允许用户输入house信息,单击提交时使用save\u house保存信息。这也会将用户重定向到add_rooms页面,在那里他们可以向房屋中添加房间 add_rooms显示正确,但当我单击submit时,出现以下错误: RuntimeError in Calculator#add_r
房间
,房间具有灯光
和小型家电
的嵌套属性。我有一个计算器
控制器,这是最终用户访问应用程序的方式
我的问题是,我无法从计算器
获取添加房间
以正确渲染和提交的部分。初始页面允许用户输入house
信息,单击提交时使用save\u house
保存信息。这也会将用户重定向到add_rooms
页面,在那里他们可以向房屋中添加房间
add_rooms
显示正确,但当我单击submit时,出现以下错误:
RuntimeError in Calculator#add_room
Showing app/views/calculator/add_rooms.html.erb where line #2 raised:
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
Extracted source (around line #2):
1: <div id="addRooms">
2: <p>House id is <%= @house.id %></p>
3:
4: <h3>Your rooms:</h3>
5: <% if @house.rooms %>
RAILS_ROOT: C:/Users/ryan/Downloads/react
Application Trace | Framework Trace | Full Trace
C:/Users/ryan/Downloads/react/app/views/calculator/add_rooms.html.erb:2:in `_run_erb_app47views47calculator47add_rooms46html46erb'
C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb:36:in `add_room'
C:/Users/ryan/Downloads/react/app/controllers/calculator_controller.rb:33:in `add_room'
app/models/house.rb
应用程序\u helper.rb
谢谢,Ryan出于好奇,为什么不让house接受房间的嵌套属性。这将使您的控制器代码更简单,因为添加许多房间、灯光和小家电就像只执行@house.update_属性(params[:house])一样简单。然而,这不是一个有帮助的答案,因为如果你做出改变,你仍然会有你目前的问题 您的第一个错误来自app/views/calculator/\u room\u form.html.erb的第一行
{:action=>:add_room,:id=>@house}do | form |%>
您没有为对象提供表单,\u,因此由add\u child调用的新\u child\u fields方法
_link正在尝试调用Nil类上的reflect_on_关联
解决办法是将线路改为
{:action=>:add_room}do | form |%>
这使您可以简化控制器,因为与房屋关联的房间已经传递给它
def add_room
@room = Room.new(params[:room])
@house = @room.house
respond_to do |format|
if @room.save
flash[:notice] = "Room \"#...@room.name}\" was successfully added."
format.html { render :action => 'add_rooms' }
format.xml { render :xml => @room, :status => :created, :location => @room }
else
format.html { render :action => 'add_rooms' }
format.xml { render :xml => @room.errors, :status => :unprocessable_entity }
end
end
rescue ActiveRecord::RecordNotFound
logger.error("Attempt to access invalid house #{params[:id]}")
flash[:notice] = "You must create a house before adding a room"
redirect_to :action => 'index'
end
我相信你的第二个错误也是同样的问题。但是,由于调用has_many访问器而不是生成nil,因此传递的是空数组,这解释了错误消息的差异。同样,解决方案是在渲染之前构建一个轻小型设备(如果还没有)
<h3>Lights</h3>
<% form.object.lights.build if form.object.lights.empty? %>
<% form.fields_for :lights do |light_form| %>
<%= render :partial => 'rooms/light', :locals => { :form => light_form } %>
<% end %>
<p class="addLink"><%= add_child_link "[+] Add new light", form, :lights %></p>
<h3>Small Appliances</h3>
<% form.object.small_appliances.build if form.object.small_appliances.empty? %>
<% form.fields_for :small_appliances do |sm_appl_form| %>
<%= render :partial => 'rooms/small_appliance', :locals => { :form => sm_appl_form } %>
<% end %>
新的_child_字段假定_lightpartial位于app/views/calculators文件夹中
解决方案是将light和small_appliances部分移动到此文件夹,或修改您的助手方法以接受部分选项。感谢您的回复。我曾经考虑过将
room
作为house
的嵌套属性,但没有这样做,因为我想在单独的页面上创建它们。我做了这些更改,但现在我得到了一个新的错误,我已将其添加到原始帖子中。解决了您的新错误。您得到的错误消息是您的错误的最佳指示。如果不是语法错误,90%的情况下是Rails做出了错误的假设。使用错误消息找出与您期望的工作方式不符的地方。谢谢。我移动了部分,现在所有内容都正确显示。但是,一旦我单击“添加房间”页面的提交,就会出现新的错误显示app/views/calculator/add_rooms.html.erb,其中第2行出现:为nil调用id,这将错误地为4——如果您确实想要nil的id,请在line上使用object_id
。我不明白为什么它将nil作为房屋
id传递,因为它在第一次加载添加房间
时显示了房屋
id。我已将原始帖子更新为当前问题和当前代码。修复了它。谢谢你的帮助。
class CalculatorController < ApplicationController
def index
end
def save_house
@house = House.new(params[:house])
respond_to do |format|
if @house.save
format.html { render :action => 'add_rooms', :id => @house }
format.xml { render :xml => @house, :status => :created, :location => @house }
else
format.html { render :action => 'index' }
format.xml { render :xml => @house.errors, :status => :unprocessable_entity }
end
end
end
def add_rooms
@house = House.find(params[:id])
@rooms = Room.find_by_house_id(@house.id)
rescue ActiveRecord::RecordNotFound
logger.error("Attempt to access invalid house #{params[:id]}")
flash[:notice] = "You must create a house before adding rooms"
redirect_to :action => 'index'
end
def add_room
@room = Room.new(params[:room])
@house = @room.house
respond_to do |format|
if @room.save
flash[:notice] = "Room \"#...@room.name}\" was successfully added."
format.html { render :action => 'add_rooms' }
format.xml { render :xml => @room, :status => :created, :location => @room }
else
format.html { render :action => 'add_rooms' }
format.xml { render :xml => @room.errors, :status => :unprocessable_entity }
end
end
rescue ActiveRecord::RecordNotFound
logger.error("Attempt to access invalid house #{params[:id]}")
flash[:notice] = "You must create a house before adding a room"
redirect_to :action => 'index'
end
def report
flash[:notice] = nil
@house = House.find(params[:id])
@rooms = Room.find_by_house_id(@house.id)
rescue ActiveRecord::RecordNotFound
logger.error("Attempt to access invalid house #{params[:id]}")
flash[:notice] = "You must create a house before generating a report"
redirect_to :action => 'index'
end
end
<div id="addRooms">
<p>House id is <%= @house.id %></p>
<h3>Your rooms:</h3>
<% if @house.rooms %>
<ul>
<% for room in @house.rooms %>
<li>
<%= h room.name %> has <%= h room.number_of_bulbs %>
<%= h room.wattage_of_bulbs %> watt bulbs, in use for
<%= h room.usage_hours %> hours per day.
</li>
<% end %>
</ul>
<% else %>
<p>You have not added any rooms yet</p>
<% end %>
<%= render :partial => 'rooms/room_form' %>
<br />
</div>
<%= button_to "Continue to report", :action => "report", :id => @house %>
<% form_for :room, @house.rooms.build, :url => { :action => :add_room } do |form| %>
<%= form.error_messages %>
<p>
<%= form.label :name %><br />
<%= form.text_field :name %>
</p>
<h3>Lights</h3>
<% form.object.lights.build if form.object.lights.empty? %>
<% form.fields_for :lights do |light_form| %>
<%= render :partial => "light", :locals => { :form => light_form } %>
<% end %>
<p class="addLink"><%= add_child_link "[+] Add new light", form, :lights %></p>
<h3>Small Appliances</h3>
<% form.object.small_appliances.build if form.object.small_appliances.empty? %>
<% form.fields_for :small_appliances do |sm_appl_form| %>
<%= render :partial => "small_appliance", :locals => { :form => sm_appl_form } %>
<% end %>
<p class="addLink"><%= add_child_link "[+] Add new small appliance", form, :small_appliances %></p>
<p><%= form.submit "Submit" %></p>
<% end %>
module ApplicationHelper
def remove_child_link(name, form)
form.hidden_field(:_delete) + link_to_function(name, "remove_fields(this)")
end
def add_child_link(name, form, method)
fields = new_child_fields(form, method)
link_to_function(name, h("insert_fields(this, \"#{method}\", \"#{escape_javascript(fields)}\")"))
end
def new_child_fields(form_builder, method, options = {})
options[:object] ||= form_builder.object.class.reflect_on_association(method).klass.new
options[:partial] ||= method.to_s.singularize
options[:form_builder_local] ||= :form
form_builder.fields_for(method, options[:object], :child_index => "new_#{method}") do |form|
render(:partial => options[:partial], :locals => { options[:form_builder_local] => form })
end
end
end
def add_room
@room = Room.new(params[:room])
@house = @room.house
respond_to do |format|
if @room.save
flash[:notice] = "Room \"#...@room.name}\" was successfully added."
format.html { render :action => 'add_rooms' }
format.xml { render :xml => @room, :status => :created, :location => @room }
else
format.html { render :action => 'add_rooms' }
format.xml { render :xml => @room.errors, :status => :unprocessable_entity }
end
end
rescue ActiveRecord::RecordNotFound
logger.error("Attempt to access invalid house #{params[:id]}")
flash[:notice] = "You must create a house before adding a room"
redirect_to :action => 'index'
end
<h3>Lights</h3>
<% form.object.lights.build if form.object.lights.empty? %>
<% form.fields_for :lights do |light_form| %>
<%= render :partial => 'rooms/light', :locals => { :form => light_form } %>
<% end %>
<p class="addLink"><%= add_child_link "[+] Add new light", form, :lights %></p>
<h3>Small Appliances</h3>
<% form.object.small_appliances.build if form.object.small_appliances.empty? %>
<% form.fields_for :small_appliances do |sm_appl_form| %>
<%= render :partial => 'rooms/small_appliance', :locals => { :form => sm_appl_form } %>
<% end %>
def new_child_fields(form_builder, method, options = {})
options[:object] ||= form_builder.object.class.reflect_on_association(method).klass.new
# specifically this line.
options[:partial] ||= method.to_s.singularize
options[:form_builder_local] ||= :form
form_builder.fields_for(method, options[:object], :child_index => "new_#{method}") do |form|
render(:partial => options[:partial], :locals => { options[:form_builder_local] => form })
end
end