【问题标题】:Assigning id's from another table to input using nested forms in Rails 4使用 Rails 4 中的嵌套表单将另一个表中的 id 分配给输入
【发布时间】:2015-04-28 22:34:12
【问题描述】:

我在第一次体验 Rails / Web 开发方面已经有几周的时间了,但遇到了一个我似乎无法在溢出或锻炼中找到的障碍。我觉得我快到了,但还不完全……

我正在为一家慈善机构开发员工网站,并且需要对新员工进行分类,以便为每个员工分配权限。目前,我有一个创建新员工的员工新视图,并有一个嵌套表单用于在其中输入以同时添加员工的部门。但是,目前为每个新员工生成一个新部门。因此,如果我创建了 2 个部门为“Admin”的员工,我最终会得到 2 个具有不同 type_id 的 Admin 部门,而不是将它们分配给相同的部门。假设部门已经存在,我如何在表单中创建员工的类型/部门时分配它。在控制台中我可以这样做:

s = Staff.create(staff_name: "John", staff_email: "John@example.com", password: "asdsadsad")
t = Type.create(department: "Admin")
s.types << t

到目前为止,我的人员和类型(类别)模型看起来像这样(删除了不相关的部分),我在其中添加了accepts_nested_attributes_for:

class Staff < ActiveRecord::Base
  has_many :staff_types
  has_many :types, through: :staff_types
  accepts_nested_attributes_for :types
  validates :staff_name, presence: true, length: { minimum: 5, maximum: 50}
  has_secure_password
end

class StaffType < ActiveRecord::Base
  belongs_to :staff
  belongs_to :type
end

class Type < ActiveRecord::Base
  validates :department, presence: true, length: { minimum: 2, maximum: 25 }
  has_many :staff_types
  has_many :staffs, through: :staff_types
end

我已将类型变量列入白名单的人员控制器如下:

class StaffsController < ApplicationController
  before_action :set_staff, only: [:edit, :update, :show]

  def new
    @staff = Staff.new
    1.times { @staff.types.build}
  end 

  def create
    @staff = Staff.new(staff_params)
    if @staff.save
      flash[:success] = "Your account has been created successfully"
      redirect_to staff_path(@staff)
    else
      render 'new'
    end
  end

  private

  def staff_params
    params.require(:staff).permit(:staff_name, :staff_email, types_attributes: [:id, :department])
  end
end

最后我的观点如下,我在 form_for 助手中嵌入了嵌套表单:

<div class="row">
   <div class="well col-md-8 col-md-offset-2">
     <%= form_for @staff do |f| %>

       <%= f.label :staff_name %>
       <%= f.text_field :staff_name %>

       <%= f.label :staff_email %>
       <%= f.email_field :staff_email %>

       <%= f.label :password %>
       <%= f.password_field :password %>

       <%= f.label :password_confirmation %>
       <%= f.password_field :password_confirmation %>

       <%= f.fields_for :types, @staff.types do |types_form| %>
         <%= types_form.label :department %>
         <%= types_form.text_field :department %>
       <% end %>

       <%= f.submit(@staff.new_record? ? "Submit Profile" : "Submit Edited Profile", class: "btn btn-success") %>
    <% end %>
  </div>
</div>

【问题讨论】:

    标签: ruby-on-rails forms ruby-on-rails-4 nested many-to-many


    【解决方案1】:

    每次提交新员工时都会创建新记录的原因是因为您实际上并没有让控制器知道员工的类型可能是现有类型。所以现在的问题是,编辑这个句子的最佳方法是什么

    方法一:在多选中选择员工类型

    如果您有一定数量的类型,并且您已经插入了它们,并且不需要更改它们,那么您可以使用多选。为此,请在您的代码中替换以下代码...

    <%= f.fields_for :types, @staff.types do |types_form| %>
      <%= types_form.label :department %>
      <%= types_form.text_field :department %>
    <% end %>
    

    ...与:

    <%= f.collection_select :type_ids, Type.all, :id, :department,
        { selected: @staff.type_ids }, { multiple: true, size: Type.all.size } %>
    

    然后,在您的控制器中,将您的 staff_params 方法更改为:

    def staff_params
      params.require(:staff).permit(:staff_name, :staff_email, type_ids: [])
    end
    

    使用此方法,您可以从所有可能的类型中为每位员工选择多种类型。假设您在创建工作人员时不需要创建类型,我建议您继续使用此成员。

    您也可以使用复选框而不是多选,但我不会在此答案中介绍这一点。您可以阅读有关复选框的信息here

    方法 #2:确定控制器中哪些类型是新的

    如果您想保留输入员工类型的能力,这会有点棘手,但我认为您可以将所有逻辑保留在控制器的 create 方法中。目前,在该方法中,只要获取其参数,您就可以保存工作人员。您可以首先检查传递的任何类型是否是新的(即工作人员尚未与该类型关联),然后如果至少有一个,则检查每个类型是否为现有类型。更新您的 create 方法以读取:

    def create
      @staff = Staff.assign_attributes(staff_params)
    
      @staff.staff_types.each_with_index do |staff_type, index|
        if (type = Type.find_by(department: staff_type.type.department)).present?
          @staff.staff_type[index].assign_attribute(:id, type.id)
        end
      end
    
      if @staff.save
        flash[:success] = "Your account has been created successfully"
        redirect_to staff_path(@staff)
      else
        render 'new'
      end
    end
    

    我应该指出,可能有更好的方法来做到这一点,希望这个答案能鼓励某人发布更好的方法,但这个答案应该适合你的目的。

    【讨论】:

    • 谢谢@Joe,第一种方法效果很好。由于允许站点用户查看哪些部分以及他们可以执行哪些操作将取决于他们的部门,因此在站点上线的实例中只会创建有限的部门列表。此外,无论如何,只有管理员用户才能访问员工新视图。对于像我一样不确定collection_select如何工作的人,请参阅此问题link
    • 嘿,@Joe,有没有办法使用下拉菜单而不是列表(我试过删除多个选项,或者将大小设置为 1,但它没有之后似乎更长的工作时间)。还有一种方法可以选择多个选项吗?谢谢
    • 我不认为你真的可以有一个多选下拉菜单。那是你要找的吗?此外,使用multiple: true,您应该能够选择多种类型。不是这样吗?
    • 啊,不抱歉,我的意思是两者分开,只是为了将来的知识。所以只需一个选择下拉菜单。而对于多选,确实如此!我目前正在 Windows 工作笔记本电脑、家里的 mac 和基于 ubuntu 的云环境之间切换以进行大多数开发,所以在我之前的困惑状态下似乎只是按住了错误的多选键!似乎在周末慢慢失去它。再次感谢
    猜你喜欢
    • 2021-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-01
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多