【问题标题】:Rails nested fields when associating User to Roles model将用户关联到角色模型时的 Rails 嵌套字段
【发布时间】:2021-09-02 19:41:26
【问题描述】:

我在这里的嵌套字段有一些问题。我在其他视图/控制器中使用嵌套字段没有问题。

我正在尝试将角色表中的角色关联到用户表。

我的榜样是这样的:

class Role < ApplicationRecord
  has_many :users
end

我的用户模型有这个:

class User < ApplicationRecord
  belongs_to :role, optional: true
  accepts_nested_attributes_for :role

...

之所以设置为可选,是因为当前用户还没有角色,我需要先将其应用到那些用户(目前只有两个用户在生产中,所以没关系)

我的用户控制器对于允许的属性和更新是这样的

class Admin::UsersController < ApplicationController
  before_action :set_user, only: [:edit, :update, :destroy]
...
  def edit
  end

  def update
    respond_to do |format|
      if @user.update(user_params)
        format.html { redirect_to admin_users_url, notice: 'User Account was successfully updated.' }
      else
        format.html { render :edit }
      end
    end
  end

  private

  def user_params
    params.require(:user).permit(:first_name, :last_name, :email, :password, roles_attributes: [:name])
  end

  def set_user
      @user = User.find(params[:id])
    end
end

以及更新用户角色的表单:

.container.p-4
    %h1 Edit User Information

    = form_for([:admin, @user]) do |f|
        - if @user.errors.any?
            #error_explanation
            %h2
            = pluralize(@user.errors.count, "error")
            prohibited this event from being saved:
            %ul
                - @user.errors.each do |error|
                    %li= error.full_message


        .row.mb-4
            .col
                = f.label :first_name, "First Name"
                = f.text_field :first_name, class: "form-control border border-dark"
            .col
                = f.label :last_name, "Last Name"
                = f.text_field :last_name, class: "form-control border border-dark"
        .form-group.mb-4
            = f.label :email, "Email Address"
            = f.email_field :email, class: "form-control border border-dark"

        %h2 User Role
        .form-group.mb-4
            = f.fields_for :roles do |f|
                = f.check_box :name, checked: false, value: "admin"
                = f.label :name, "Admin"
                
        .form-group.p-4.text-center
            = f.submit
            

当我在检查“管理员”后点击更新时,终端读数是 :roles 是不允许的。

我有一个单独的角色控制器,允许我定义将用户关联到的角色。 Roles 表只有 name:string 和 user:references。

所以我不确定为什么不允许这样做。

【问题讨论】:

  • 请分享MWE您可以查看here了解更多信息

标签: ruby-on-rails


【解决方案1】:

你真正想要的是一个连接表来避免非规范化:

class User < ApplicationRecord
  has_many :user_roles
  has_many :roles, through: :user_roles
  accepts_nested_attributes_for :roles
end

class UserRole < ApplicationRecord
  belongs_to :user
  belongs_to :role
end

class Role < ApplicationRecord
  validates :name, uniqueness: true
  has_many :user_roles
  has_many :users, through: :user_roles
end

这将允许您将多个用户分配给一个角色,而无需为每一行复制字符串“admin”,并且如果一行包含“Admin”而不是冒着非规范化和可能发生的错误的风险。您可以将现有列表中的角色分配给具有以下条件的用户:

<% form_for([:admin, @user]) do |form| %>
  <div class="field">
    <%= form.label :role_ids, 'Roles' %>
    <%= form.collection_select(:role_ids, Role.all, :id, :name, multiple: true) %>
  </div>

  # ...
<% end %>
def user_params
  params.require(:user)
        .permit(
          :first_name, :last_name, :email, :password, 
          role_ids: []
        )
end

如果您真的希望能够在创建用户时动态创建新角色,您可以使用嵌套属性。我真的会直接使用 AJAX,因为它可以让您在单独的控制器中处理授权逻辑。您可能需要考虑您可能希望让某些用户分配角色但不发明新的角色定义。

【讨论】:

  • 如果您想详细了解原因,请查看答案to this question I asked on SE Software Engineering a while back。
  • 我在你的回答中按照上面描述的方式设置了它,但仍然在终端中得到这个:“Unpermitted parameter::role_ids”。我更新了角色列表以包括角色模型本身的 Admin 和 Basic 以查看 multiple: true 是否有效,但事实并非如此。它只让我选择一个角色。当我在他的用户表单上点击更新时,即使我按照您上面描述的方式设置了它,我也会收到未经许可的错误。
  • 传入的参数散列是什么样的?我不太确定这个错误是怎么回事。 role_ids: [] 将允许作为user[:user_ids] 传递的允许标量值数组(在本例中为字符串)。您是否可能在fields_for 产生的范围表单构建器上调用f.collection select?
  • Admin::UsersController#update 作为 HTML 参数处理:{"authenticity_token"=>"[FILTERED]", "user"=>{"first_name"=>"Patrick", "last_name" =>“Vellia”、“email”=>“已编辑”、“role_ids”=>“35db991e-c1c0-4bc3-a49c-d6c189857d8e”}、“commit”=>“更新用户”、“id”=>“6fa69dc9 -74ee-4d85-8038-0617f83d597b"} 用户负载 (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2 [[ "id", "41f0d4cc-5ef0-4994-8a5f-3786faf33eec"], ["LIMIT", 1]]
  • 用户负载 (1.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", "6fa69dc9-74ee-4d85-8038 -0617f83d597b"], ["LIMIT", 1]] ↳ app/controllers/admin/users_controller.rb:46:in `set_user' 未经允许的参数::role_ids
【解决方案2】:

坚持使用角色 id 并使用引导选择选择器,以下效果也很好,甚至预先选择了已经设置的角色

 = f.select :role_ids, options_for_select(Role.all.map{|role| [role.name, role.id]}, @user.role_ids), {},  {:multiple => true, inlcude_blank: false, class: "form-control input-sm selectpicker"}

和控制器:

module Backend
  class UsersController < ApplicationController
    before_action :set_user, only: %i[edit update]
    def index
      @users = User.all
    end

    def edit
      @user.roles.build unless @user.roles.any?
    end

    def update
      if @user.update user_params
        redirect_to backend_users_path(@user), notice: 'Rollen erfolgreich aktualisiert'
      else
        render :edit
      end
    end

    private

    def set_user
      @user = User.find(params[:id])
    end

    def user_params
      params.require(:user).permit(:id, role_ids: [])
    end
  end
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多