Rails 5.1 form_with 三重嵌套形式 child (grandchild??) of child 不继承属性且不保存
Rails 5.1 form_with triple nested form child (grandchild??) of child not inheriting attribute and not saving
我的应用程序(基于 Hartl Rails 5 教程)有一个 has_many Rooms 的 User 模型,以及 has_many Students 的 Room 模型。我有一个表格供用户与学生(具有:name 属性)创建一个房间(具有:number 属性)。尽管用户创建表单与房间创建表单是分开的,但我使用了此示例中的关联代码:https://medium.com/@mindovermiles262/triple-nested-forms-in-rails-dedbcccb5799.
当我提交表单时 returns 错误 "Students room can't be blank"。 Room 和 Student 之间的关联在某处丢失了。以下是带有错误消息的已提交表单的屏幕截图:
这里是相关的user.rb代码:
class User < ApplicationRecord
has_many :rooms, dependent: :destroy
has_many :students, through: :rooms
accepts_nested_attributes_for :rooms, allow_destroy: true
accepts_nested_attributes_for :students, allow_destroy: true
...
end
users_controller.rb
private
def user_params
params.require(:user).permit(:name, :email, :school, :password,
:password_confirmation,
:rooms_attributes => [:id, :number,
:students_attributes => [:id, :name]
] )
end
room.rb:
class Room < ApplicationRecord
belongs_to :user, optional: true
has_many :students, dependent: :destroy, inverse_of:
:room
accepts_nested_attributes_for :students, allow_destroy: true
default_scope -> { order(number: :asc) }
validates :number, presence: true
validates :user_id, presence: true
end
rooms_controller.rb
class RoomsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy, :edit, :update, :show]
before_action :correct_user, only: [:show]
def new
@room = current_user.rooms.build if logged_in?
@student = @room.students.build
end
def create
@room = current_user.rooms.build(room_params)
if @room.save && @student.save
flash[:success] = "New class created."
redirect_to current_user
else
render 'rooms/new'
end
end
def destroy
end
def edit
end
def update
@room = Room.find(params[:id])
if @room.update_attribute(room_params)
redirect_to @room
flash[:success] = "Class updated."
end
end
def show
@user = User.find(params[:id])
@rooms = @user.rooms
# @number = @rooms.number
end
private
def room_params
params.require(:room).permit(:number, :students_attributes => [:id, :name])
end
end
来自sessions_helper.rb的current_user方法:
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
@current_user = user
end
end
end
student.rb:
class Student < ApplicationRecord
belongs_to :room, optional: true, inverse_of: :students
validates :name, presence: true, length: { maximum: 50 }
validates :room_id, presence: true
end
students_controller.rb
class StudentsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy, :edit, :update, :show]
before_action :correct_user, only: [:show]
def new
@room = current_user.rooms.build if logged_in?
@student = @room.students.build
end
def create
if @student.save
flash[:success] = "New student created."
redirect_to current_user
else
render 'students/new'
end
end
def show
end
private
def student_params
params.require(:student).permit(:name)
end
end
房间创建表单,rooms\new。html.erb:
<% provide(:title, 'Create a class') %>
<h1>Create a class</h1>
<%= form_with(model: @room, local: true) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :number, "Class number" %>
<%= f.number_field :number, class: 'form-control' %>
<%= f.fields_for :students do |s| %>
<%= s.label :name, "Student name" %>
<%= s.text_field :name, class: 'form-control' %>
<% end %>
<%= f.submit "Create class", class: "btn btn-primary" %>
<%= params.inspect %>
<% end %>
config\routes.rb:
Rails.application.routes.draw do
get 'password_resets/new'
get 'password_resets/edit'
get 'sessions/new'
root 'static_pages#home'
get '/signup', to: 'users#new'
post '/signup', to: 'users#create'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
get '/showall', to: 'users#index'
get '/createroom', to: 'rooms#new'
post '/createroom', to: 'rooms#create'
get 'createstudent', to: 'students#new'
get 'createstudent', to: 'students#create'
resources :users
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :rooms
resources :students
end
和development.log:
Processing by RoomsController#new as
Rendering rooms/new.html.erb within layouts/application
Rendered shared/_error_messages.html.erb (1.0ms)
Rendered rooms/new.html.erb within layouts/application (388.0ms)
Rendered layouts/_rails_default.html.erb (244.0ms)
Rendered layouts/_shim.html.erb (1.0ms)
Rendered layouts/_header.html.erb (1.0ms)
Rendered layouts/_footer.html.erb (1.0ms)
Completed 200 OK in 2938ms (Views: 2513.1ms | ActiveRecord: 12.0ms)
Processing by RoomsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"henh7dclciYRcZE6xRFfdSHBPQuRmO03eXXEsmRkSSI25uqkMSo6WFwthixdEzDNSNjY5pPmcIYwi5nmFZirMg==", "room"=>{"number"=>"200", "students_attributes"=>{"0"=>{"name"=>"Roo"}}}, "commit"=>"Create class"}
Rendering rooms/new.html.erb within layouts/application
Rendered shared/_error_messages.html.erb (1.0ms)
Rendered rooms/new.html.erb within layouts/application (35.0ms)
Rendered layouts/_rails_default.html.erb (237.0ms)
Rendered layouts/_shim.html.erb (0.0ms)
Rendered layouts/_header.html.erb (1.0ms)
Rendered layouts/_footer.html.erb (1.0ms)
Completed 200 OK in 3283ms (Views: 494.7ms | ActiveRecord: 3.0ms)
I added :inverse_of per but that made no difference. This () does not appear relevant, b/c it involves a parent with two different children, not a parent 有一个 child 也有一个 child.
在此先感谢您的帮助。
编辑:
我终于意识到错误消息 "Students room can't be blank" 指的是 room_id 是空白的。我从 student.rb 中删除了以下行:
验证:room_id,存在:真
我以为
belongs_to:房间,可选:true
会暂时绕过验证方法,但我错了。
删除
验证:room_id,可选:真
是解决方案。
我的应用程序(基于 Hartl Rails 5 教程)有一个 has_many Rooms 的 User 模型,以及 has_many Students 的 Room 模型。我有一个表格供用户与学生(具有:name 属性)创建一个房间(具有:number 属性)。尽管用户创建表单与房间创建表单是分开的,但我使用了此示例中的关联代码:https://medium.com/@mindovermiles262/triple-nested-forms-in-rails-dedbcccb5799.
当我提交表单时 returns 错误 "Students room can't be blank"。 Room 和 Student 之间的关联在某处丢失了。以下是带有错误消息的已提交表单的屏幕截图:
这里是相关的user.rb代码:
class User < ApplicationRecord
has_many :rooms, dependent: :destroy
has_many :students, through: :rooms
accepts_nested_attributes_for :rooms, allow_destroy: true
accepts_nested_attributes_for :students, allow_destroy: true
...
end
users_controller.rb
private
def user_params
params.require(:user).permit(:name, :email, :school, :password,
:password_confirmation,
:rooms_attributes => [:id, :number,
:students_attributes => [:id, :name]
] )
end
room.rb:
class Room < ApplicationRecord
belongs_to :user, optional: true
has_many :students, dependent: :destroy, inverse_of:
:room
accepts_nested_attributes_for :students, allow_destroy: true
default_scope -> { order(number: :asc) }
validates :number, presence: true
validates :user_id, presence: true
end
rooms_controller.rb
class RoomsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy, :edit, :update, :show]
before_action :correct_user, only: [:show]
def new
@room = current_user.rooms.build if logged_in?
@student = @room.students.build
end
def create
@room = current_user.rooms.build(room_params)
if @room.save && @student.save
flash[:success] = "New class created."
redirect_to current_user
else
render 'rooms/new'
end
end
def destroy
end
def edit
end
def update
@room = Room.find(params[:id])
if @room.update_attribute(room_params)
redirect_to @room
flash[:success] = "Class updated."
end
end
def show
@user = User.find(params[:id])
@rooms = @user.rooms
# @number = @rooms.number
end
private
def room_params
params.require(:room).permit(:number, :students_attributes => [:id, :name])
end
end
来自sessions_helper.rb的current_user方法:
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
@current_user = user
end
end
end
student.rb:
class Student < ApplicationRecord
belongs_to :room, optional: true, inverse_of: :students
validates :name, presence: true, length: { maximum: 50 }
validates :room_id, presence: true
end
students_controller.rb
class StudentsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy, :edit, :update, :show]
before_action :correct_user, only: [:show]
def new
@room = current_user.rooms.build if logged_in?
@student = @room.students.build
end
def create
if @student.save
flash[:success] = "New student created."
redirect_to current_user
else
render 'students/new'
end
end
def show
end
private
def student_params
params.require(:student).permit(:name)
end
end
房间创建表单,rooms\new。html.erb:
<% provide(:title, 'Create a class') %>
<h1>Create a class</h1>
<%= form_with(model: @room, local: true) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :number, "Class number" %>
<%= f.number_field :number, class: 'form-control' %>
<%= f.fields_for :students do |s| %>
<%= s.label :name, "Student name" %>
<%= s.text_field :name, class: 'form-control' %>
<% end %>
<%= f.submit "Create class", class: "btn btn-primary" %>
<%= params.inspect %>
<% end %>
config\routes.rb:
Rails.application.routes.draw do
get 'password_resets/new'
get 'password_resets/edit'
get 'sessions/new'
root 'static_pages#home'
get '/signup', to: 'users#new'
post '/signup', to: 'users#create'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
get '/showall', to: 'users#index'
get '/createroom', to: 'rooms#new'
post '/createroom', to: 'rooms#create'
get 'createstudent', to: 'students#new'
get 'createstudent', to: 'students#create'
resources :users
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :rooms
resources :students
end
和development.log:
Processing by RoomsController#new as
Rendering rooms/new.html.erb within layouts/application
Rendered shared/_error_messages.html.erb (1.0ms)
Rendered rooms/new.html.erb within layouts/application (388.0ms)
Rendered layouts/_rails_default.html.erb (244.0ms)
Rendered layouts/_shim.html.erb (1.0ms)
Rendered layouts/_header.html.erb (1.0ms)
Rendered layouts/_footer.html.erb (1.0ms)
Completed 200 OK in 2938ms (Views: 2513.1ms | ActiveRecord: 12.0ms)
Processing by RoomsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"henh7dclciYRcZE6xRFfdSHBPQuRmO03eXXEsmRkSSI25uqkMSo6WFwthixdEzDNSNjY5pPmcIYwi5nmFZirMg==", "room"=>{"number"=>"200", "students_attributes"=>{"0"=>{"name"=>"Roo"}}}, "commit"=>"Create class"}
Rendering rooms/new.html.erb within layouts/application
Rendered shared/_error_messages.html.erb (1.0ms)
Rendered rooms/new.html.erb within layouts/application (35.0ms)
Rendered layouts/_rails_default.html.erb (237.0ms)
Rendered layouts/_shim.html.erb (0.0ms)
Rendered layouts/_header.html.erb (1.0ms)
Rendered layouts/_footer.html.erb (1.0ms)
Completed 200 OK in 3283ms (Views: 494.7ms | ActiveRecord: 3.0ms)
I added :inverse_of per
在此先感谢您的帮助。
编辑:
我终于意识到错误消息 "Students room can't be blank" 指的是 room_id 是空白的。我从 student.rb 中删除了以下行:
验证:room_id,存在:真
我以为
belongs_to:房间,可选:true
会暂时绕过验证方法,但我错了。
删除
验证:room_id,可选:真
是解决方案。