如何在不通过表单传递数据的情况下更新嵌套资源
How do I update a nested resource without passing the data via a form
型号
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :roles, :dependent => :destroy, :inverse_of => :user
has_many :companies, :through => :roles
accepts_nested_attributes_for :roles, :limit => 1, :allow_destroy => true
end
class Role < ActiveRecord::Base
belongs_to :user, :inverse_of => :roles
belongs_to :company, :inverse_of => :roles
accepts_nested_attributes_for :company
end
class Company < ActiveRecord::Base
has_many :roles, :dependent => :destroy, :inverse_of => :user
has_many :users, :through => :roles
validates :name, presence: true
end
自定义设计注册控制器
class RegistrationsController < Devise::RegistrationsController
# GET /resource/sign_up
def new
build_resource({})
@role = resource.roles.build(role: "owner", active: 1, default_role: 1)
@company = @role.build_company
set_minimum_password_length
yield resource if block_given?
respond_with self.resource
end
protected
def sign_up_params
params.require(:user).permit(:email, :password, :password_confirmation, roles_attributes: [ company_attributes: [ :id, :name ] ] )
end
end
HTML
<%= form_for(resource, :html => {:class => "form-signin" }, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render partial: "shared/flash" %>
<%= devise_error_messages! %>
<h1 class="form-signin-heading text-muted">Register</h1>
<%= f.email_field :email, class: "form-control", placeholder: "Email", autofocus: true %>
<%= f.password_field :password, class: "form-control", placeholder: "Password", autocomplete: "off" %>
<%= f.password_field :password_confirmation, class: "form-control", placeholder: "Password Confirmation", autocomplete: "off" %>
<%= f.fields_for :roles, resource.roles.build do |r| %>
<%= r.fields_for :company, resource.roles.build.build_company do |c| %>
<%= c.text_field :name, class: "form-control", placeholder: "Company", autocomplete: "off" %>
<% end %>
<% end %>
<button class="btn btn-lg btn-primary btn-block" type="submit">
Register
</button>
<% end %>
这有效 - 我的中间角色是使用 id_user 和 id_company 创建的。问题是我想在新创建的角色中设置一些额外的字段。例如,我有一个要设置为 'owner' 的 :role 列,因为这是一家全新的公司,注册的用户是所有者。
我想在控制器中执行此操作以防止用户提交的表单出现任何批量分配问题。
我是否需要在自定义设计注册控制器中以某种方式进行设置并创建完整的自定义创建操作?
我承认我可能没有很好地解释这一点,因为我在整个嵌套表单和活动记录等方面还是个新手。
更新
它不是很漂亮,但我只是将它粘贴在我的新控制器的末尾:
def set_minimum_password_length
if devise_mapping.validatable?
@minimum_password_length = resource_class.password_length.min
end
end
更新 2
我复制了主代码与当前版本代码。一旦我修好了,一切都很好。
我会覆盖创建操作,以便您可以对角色属性进行硬编码(并且用户不能乱用)。
您在新操作中分配它们,但您需要有隐藏字段,以便它们被传递以创建并保存到数据库中。但是,这不是一个好主意,因为任何人都可以编辑 HTML 并更改这些值。最好在创建操作中执行此操作,而不是像这样:
class RegistrationsController < Devise::RegistrationsController
def create
build_resource(sign_up_params)
# The last role should be the one created in the form
# We set the values here so they get saved and they aren't passed in the form
resource.roles.last.assign_attributes(role: "owner", active: 1, default_role: 1)
resource.save
yield resource if block_given?
if resource.persisted?
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_flashing_format?
sign_up(resource_name, resource)
respond_with resource, location: after_sign_up_path_for(resource)
else
set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format?
expire_data_after_sign_in!
respond_with resource, location: after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
set_minimum_password_length
respond_with resource
end
end
end
型号
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :roles, :dependent => :destroy, :inverse_of => :user
has_many :companies, :through => :roles
accepts_nested_attributes_for :roles, :limit => 1, :allow_destroy => true
end
class Role < ActiveRecord::Base
belongs_to :user, :inverse_of => :roles
belongs_to :company, :inverse_of => :roles
accepts_nested_attributes_for :company
end
class Company < ActiveRecord::Base
has_many :roles, :dependent => :destroy, :inverse_of => :user
has_many :users, :through => :roles
validates :name, presence: true
end
自定义设计注册控制器
class RegistrationsController < Devise::RegistrationsController
# GET /resource/sign_up
def new
build_resource({})
@role = resource.roles.build(role: "owner", active: 1, default_role: 1)
@company = @role.build_company
set_minimum_password_length
yield resource if block_given?
respond_with self.resource
end
protected
def sign_up_params
params.require(:user).permit(:email, :password, :password_confirmation, roles_attributes: [ company_attributes: [ :id, :name ] ] )
end
end
HTML
<%= form_for(resource, :html => {:class => "form-signin" }, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render partial: "shared/flash" %>
<%= devise_error_messages! %>
<h1 class="form-signin-heading text-muted">Register</h1>
<%= f.email_field :email, class: "form-control", placeholder: "Email", autofocus: true %>
<%= f.password_field :password, class: "form-control", placeholder: "Password", autocomplete: "off" %>
<%= f.password_field :password_confirmation, class: "form-control", placeholder: "Password Confirmation", autocomplete: "off" %>
<%= f.fields_for :roles, resource.roles.build do |r| %>
<%= r.fields_for :company, resource.roles.build.build_company do |c| %>
<%= c.text_field :name, class: "form-control", placeholder: "Company", autocomplete: "off" %>
<% end %>
<% end %>
<button class="btn btn-lg btn-primary btn-block" type="submit">
Register
</button>
<% end %>
这有效 - 我的中间角色是使用 id_user 和 id_company 创建的。问题是我想在新创建的角色中设置一些额外的字段。例如,我有一个要设置为 'owner' 的 :role 列,因为这是一家全新的公司,注册的用户是所有者。
我想在控制器中执行此操作以防止用户提交的表单出现任何批量分配问题。
我是否需要在自定义设计注册控制器中以某种方式进行设置并创建完整的自定义创建操作?
我承认我可能没有很好地解释这一点,因为我在整个嵌套表单和活动记录等方面还是个新手。
更新
它不是很漂亮,但我只是将它粘贴在我的新控制器的末尾:
def set_minimum_password_length
if devise_mapping.validatable?
@minimum_password_length = resource_class.password_length.min
end
end
更新 2
我复制了主代码与当前版本代码。一旦我修好了,一切都很好。
我会覆盖创建操作,以便您可以对角色属性进行硬编码(并且用户不能乱用)。
您在新操作中分配它们,但您需要有隐藏字段,以便它们被传递以创建并保存到数据库中。但是,这不是一个好主意,因为任何人都可以编辑 HTML 并更改这些值。最好在创建操作中执行此操作,而不是像这样:
class RegistrationsController < Devise::RegistrationsController
def create
build_resource(sign_up_params)
# The last role should be the one created in the form
# We set the values here so they get saved and they aren't passed in the form
resource.roles.last.assign_attributes(role: "owner", active: 1, default_role: 1)
resource.save
yield resource if block_given?
if resource.persisted?
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_flashing_format?
sign_up(resource_name, resource)
respond_with resource, location: after_sign_up_path_for(resource)
else
set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format?
expire_data_after_sign_in!
respond_with resource, location: after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
set_minimum_password_length
respond_with resource
end
end
end