【问题标题】:How do I update the record of a Has_Many through association?如何通过关联更新 Has_Many 的记录?
【发布时间】:2012-10-08 00:02:20
【问题描述】:

我有三个模型 - UserClientTopic

用户

has_many :clients
has_many :topics, :through => :clients, :uniq => true

客户

has_and_belongs_to_many :topics

主题

has_and_belongs_to_many :clients

我要做的是在我的客户的edit 视图上,更改此客户的主题。

这是我的客户端控制器的更新操作:

  def update
        if params[:topic_ids]
             @client = current_user.clients.find(params[:id])
             @client.topic_ids = params[:client][:topic_ids]
             @client.save
        else
             @client = current_user.clients.find(params[:id])
        end


    respond_to do |format|
      if @client.update_attributes(params[:client])
        format.html { redirect_to @client, notice: 'Client was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @client.errors, status: :unprocessable_entity }
      end
    end
  end

这是日志的样子:

Started PUT "/clients/6" for 127.0.0.1 at 2012-10-07 18:56:14 -0500
Processing by ClientsController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"J172mxxCX0OdxcGm4GSPv8=", "client"=>{"name"=>"Testeeee Johnson", "email"=>"testeee@johnson.com", "phone"=>"4320981234", "firm_id"=>"1", "personal_priority"=>"1", "last_contact"=>"2012-06-08", "vote"=>"1", "vote_for_user"=>"0", "next_vote"=>"2012-10-10", "vote_ii"=>"0", "vote_ii_for_us"=>"0"}, "topic_ids"=>["2"], "commit"=>"Update Client", "id"=>"6"}
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1
  Client Load (0.1ms)  SELECT "clients".* FROM "clients" WHERE "clients"."user_id" = 1 AND "clients"."id" = ? LIMIT 1  [["id", "6"]]
  Topic Load (0.2ms)  SELECT "topics".* FROM "topics" INNER JOIN "clients_topics" ON "topics"."id" = "clients_topics"."topic_id" WHERE "clients_topics"."client_id" = 6
   (0.1ms)  begin transaction
   (0.0ms)  commit transaction
   (0.0ms)  begin transaction
   (0.0ms)  commit transaction
   (0.0ms)  begin transaction
   (0.0ms)  commit transaction
Redirected to http://localhost:3000/clients/6
Completed 302 Found in 8ms (ActiveRecord: 0.8ms)

不用说,它不会更新client.topics 的记录。

如何更新客户记录的topics 属性?

编辑 1

这就是_form 部分的样子:

<%= form_for(@client) do |f| %>
  <% if @client.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@client.errors.count, "error") %> prohibited this client from being saved:</h2>

      <ul>
      <% @client.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </div>

  <div class="field">
    <%= f.label :email %><br />
    <%= f.text_field :email %>
  </div>
  <div class="field">
    <%= f.label :firm %><br />
    <%= f.select :firm_id, Firm.all.collect { |firm| [firm.name, firm.id] }, {:include_blank => 'None'} %>
  </div>
  <div class="field">
  <h4>Topics</h4>  
    <% Topic.all.each do |topic| %>
      <% checked = @client.topics.include?(topic) %>
        <%= f.label(:name, topic.name) %> <%= check_box_tag "topic_ids[]", topic.id, checked %>
    <% end %>
  </div>
.
. - reduced for brevity
.


  <br /><br />
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

【问题讨论】:

  • 你的表单是什么样子的?
  • 更新为包含_form的相关部分。
  • 建议更新误导性的主题标题 - 这个问题是关于 HABTM 协会,而不是 HMT。

标签: ruby-on-rails ruby-on-rails-3


【解决方案1】:

无论出于何种原因,form 助手不能与 check_box 一起使用。

所以,这是有效的代码:

<%= check_box_tag "client[topic_ids][]", topic.id, checked %>

根据other answers 的类似问题,帮助器f.check_box 是模型绑定的,提供给复选框的值是表单上模型的隐含值。问题是,我不知道如何获取 form_helper 的隐含值来生成正确的标签 - 即client[topic_ids][],所以我不得不求助于check_box_tag

【讨论】:

    【解决方案2】:

    您在下面的评论中提到您还想为每个主题添加权重。 has_and_belongs_to_many 关联不再支持此功能,因此您应该使用 has_many :through 关联:

    class User < ActiveRecord::Base
      has_many :clients
    end
    
    class Clients < ActiveRecord::Base
      belongs_to :user
      has_many :client_topics
      has_many :topics, :through => :clients_topics
    end
    
    # Create this table like any other, with a "weight" field
    class ClientsTopics < ActiveRecord::Base
      belongs_to :client
      belongs_to :topic
    end
    
    class Topics < ActiveRecord::Base
      has_many :clients_topics
      has_many :clients, :through => :clients_topics
    end
    

    现在您的更新将需要删除所有现有的 clients_topics,然后循环传递传递的 topic_ids 和 weights 并将它们添加到客户端,如下所示:

    def update
      if params[:topic_ids]
         @client = current_user.clients.find(params[:id])
    
         @client.clients_topics.delete_all
    
         params[:client][:topic_ids].each_with_index do |topic_id, index|
           weight = params[:client][:weights][index]
           @client.clients_topics.create!(:topic_id => topic_id, :weight => weight)
         end
    
      else
         @client = current_user.clients.find(params[:id])
      end
    
      (etc...)
    end
    

    【讨论】:

    • 这个设置是通过多态关联吗?
    • 我不明白为什么我必须使用 each 块来删除主题,这似乎有点 hacky。 Rails 没有内置的东西来管理这些类型的关系吗?
    • 可以使用delete_all删除与客户端相关的主题,但Rails会留下记录,只是将client_id设置为nil,这通常是不可取的。
    • 不,这不是多态关联。它与 has_and_belongs_to_many 关联基本相同,但 clients_topics 表是显式创建和引用的,而不是不透明地使用它。这样做的另一个优点是您可以将其他字段添加到 clients_topics 表中,例如,每个主题对该客户端的重要性的权重。
    • 嗯...有趣的 Max,因为这正是我需要做的。添加重要性的权重。你这么说有点怪!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-09
    • 2012-01-01
    • 1970-01-01
    相关资源
    最近更新 更多