【问题标题】:Rails 4.2 - how to build a has_many, through: association with collection_selectRails 4.2 - 如何构建 has_many,通过:与 collection_select 关联
【发布时间】:2015-02-18 18:47:48
【问题描述】:

我目前正在使用带有这些模型的 Rails 4.2:

class User < ActiveRecord::Base
  has_many :user_accessories, dependent: :destroy
  accepts_nested_attributes_for :user_accessories,
    reject_if: :all_blank,
    allow_destroy: true
  has_many :accessories, through: :user_accessories
end

class UserAccessory < ActiveRecord::Base
  belongs_to :user,      required: true
  belongs_to :accessory, required: true
end

class Accessory < ActiveRecord::Base
  has_many :user_accessories, dependent: :destroy
  has_many :users, through: :user_accessories
end

这是一种简单的多对多关系,其中一个用户可以“拥有”许多配件,而一个配件可以被多个用户“拥有”。

我已按以下方式设置路线:

resources :users, only: [ :index, :show, :edit, :update, :accessories ] do
  member do
    get 'accessories'
  end
end

这样,用户可以转到他们的“配件”路径 (/users/1/accessories) 来查看他们的配件列表,以及向他们的帐户添加新的配件关联。

这是为附件路径呈现的视图:

<table>
  <thead>
    <tr>
      <th>Name</th>
    </tr>
  </thead>
  <tbody>
    <%= render @accessories %>
  </tbody>
</table>

<%= form_for( @user ) do |f| %>
  <%= f.collection_select( 
    :accessory_ids, 
    Accessory.all, 
    :id, 
    :name, { include_hidden: false }, { multiple: true } ) 
  %>

  <%= f.button "Add Equipment" %>
<% end %>

这只是渲染当前配件的表格,然后构建一个 collection_select 框,您可以在其中选择要添加的多个配件。我通过删除 Bootstrap 样式简化了这一点。

最后,以下是相关的控制器操作:

def update
  @user = User.find( params[:id] )
  if( @user.update_attributes( update_params ) )
    redirect_to @user
  else
    render 'edit'
  end
end

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

private 
  def update_params 
    params.require( :user ).permit( 
      :first_name, 
      :last_name, 
      :region_id, 
      :country, 
      :profile_image, 
      :remove_profile_image, 
      accessory_ids: [:id], 
    )
  end

现在的问题:

  1. 当我已经在数据库中手动输入了一个值时提交表单实际上只是删除 所有 当前存储的值并且不添加任何新值:

    Started PATCH "/users/nuggles" for 127.0.0.1 at 2015-02-18 13:31:50 -0500
    
    Processing by UsersController#update as HTML
    
    Parameters: {
      "utf8"=>"✓",····
      "authenticity_token"=>"my_token",·
      "user"=>{
        "accessory_ids"=>["5"]
      },· 
      "button"=>"",·
      "id"=>"nuggles"
    } 
    
    User Load (40.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1  ORDER BY "users"."id" ASC LIMIT 1  [["id", 4]] 
    
    User Load (36.4ms)  SELECT  "users".* FROM "users" WHERE "users"."slug" = $1  ORDER BY "users"."id" ASC LIMIT 1  [["slug", "nuggles"]]·
    
    (33.5ms)  BEGIN
    
    Accessory Load (34.1ms)  SELECT "accessories".* FROM "accessories" INNER JOIN "user_accessories" ON "accessories"."id" = "user_accessories"."accessory_id" WHERE "user_accessories"."user_id" = $1  [["user_id", 4]]
    
    **SQL (34.1ms)  DELETE FROM "user_accessories" WHERE "user_accessories"."user_id" = $1 AND "user_accessories"."accessory_id" IN (1, 2, 3)  [["user_id", 4]]**
    
    User Exists (37.7ms)  SELECT  1 AS one FROM "users" WHERE (LOWER("users"."username") = LOWER('Nuggles') AND "users"."id" != 4) LIMIT 1
    
    (34.2ms)  COMMIT
    
    Redirected to http://localhost:3000/users/nuggles
    Completed 302 Found in 304ms (ActiveRecord: 250.0ms)
    
  2. 提交未选择任何内容的表单时,我收到以下错误:

    param is missing or the value is empty: user
    
    private
      def update_params
        params.require( :user ).permit(
          :first_name,
          :last_name,
          :region_id,
    

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-4.2 collection-select


    【解决方案1】:

    对于任何感兴趣的人,我的表格都可以使用。这可能不是一次创建多条记录的理想方式,但它现在对我有用。

    首先,我在Users 控制器中取出了奇怪的accessories 定义。由于我将直接创建、更新和销毁用户的附件,因此连接模型应该有自己的控制器:

    class UserAccessoriesController < ApplicationController
      def index
        @user = User.friendly.find( params[:id] )
        @user_accessories = @user.user_accessories
      end
    
      def create
        @user = User.friendly.find( params[:id] )
    
        create_params[:accessory].each do |accessory|
          @user_accessory = UserAccessory.new( user: @user, accessory_id: accessory )
    
          @user_accessory.save
        end
    
        redirect_to user_accessories_path( @user )
      end
    
      def destroy
        @user_accessory = UserAccessory.friendly.find( params[:id] )
    
        @user_accessory.destroy
      end
    
      private
        def create_params
          params.require( :user_accessory ).permit(
            :id,
            accessory: []
          )
        end
    end
    

    对于UserAccessories,我只有一个视图,即索引。该页面列出了一个用户的所有配件,如果显示的用户与登录的用户相同,则显示如下表格:

      <%= form_for @user_accessories.new(), { method: :post } do |f| %>
        <div class="form-group">
          <%= f.collection_select(
                                   :accessory,
                                   # Get only accessories not on the user's account
                                   Accessory.where.not( id: @user.accessories ),
                                   :id,
                                   :name,
                                   {
                                     include_hidden: false
                                   },
                                   {
                                     multiple:    true,
                                     class:       'form-control',
                                     placeholder: 'Select Equipment to Add'
                                   }
                                 ) %>
        </div>
    
        <div class="form-group">
          <%= f.submit "Update Equipment" %>
        </div>
      <% end %>
    

    这允许用户选择多个附件(使用select2),然后在提交时在控制器中一一创建关联。

    如果有人有更好的创建用户附件的方法(即不循环通过我的参数并创建多次),那么请发表评论!

    【讨论】:

      猜你喜欢
      • 2011-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-01
      • 1970-01-01
      • 2015-01-27
      • 2012-07-09
      相关资源
      最近更新 更多