0

根据 Ryan Bates 的出色 RailsCast #258,我遇到了问题。

情况如下:

class User < ActiveRecord::Base
  has_many :capabilities,
           :dependent => :destroy
  has_many :skills, :through => :capabilities,
           :uniq => true

  has_many :raters,
           :through => :capabilities,
           :foreign_key => :rater_id,
           :uniq => true

  attr_accessible :name, :skill_tokens
  attr_reader :skill_tokens      

  def skill_tokens=(tokens)
      self.skill_ids = Skill.ids_from_tokens(tokens)
  end
end

class Capability < ActiveRecord::Base
  belongs_to :user
  belongs_to :rater, class_name: "User"
  belongs_to :skill

  validates_uniqueness_of :rater_id, :scope => [:user_id, :skill_id]
end

class Skill < ActiveRecord::Base
  has_many :capabilities
  has_many :users, :through => :capabilities,
           :uniq => true

  has_many :raters, :through => :capabilities,
           :foreign_key => :rater_id
end

该表单包含作为 id 传递的技能标记的普通文本字段:

.field  
    = f.label :skill_tokens, "Skills"
    = f.text_field :skill_tokens, data: {load: @user.skills}

因此,用户可以获得通过功能分配的许多技能。在分配技能时,还应在能力模型中跟踪评估者。

使用 Ryans 的 jquery TokenInput 示例,我创建了一个适当的表单,以允许用户使用 tokenInput 文本字段分配(和创建)技能。

现在的问题在于在保存关联之前处理数据和设置评估者。

通过一些 ruby​​ 魔术,用户模型上的 self.skill_ids 设置用于创建关联模型的 id,因此控制器操作非常简单:

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

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

显然,如果我想在能力模型上设置额外的评估者属性,那么使用 update_attributes 就不会那么容易了。

那么我怎样才能通过“rails 方式”来实现这一点——编写漂亮、可读的代码呢?任何帮助将不胜感激!

4

1 回答 1

0

你是怎么设置的rater_id

如果您计划为用户在表单上添加的每个技能接受评估者的用户输入, 我看不出您将如何使用基于令牌输入的输入字段来实现此目的。您将不得不选择一些其他类型的输入。

如果您打算将评估者设置为当前登录的用户,或者基于其他一些业务逻辑设置评估者,我的方法将覆盖skill_ids=User 模型中的方法以按照您的意愿工作,添加一个 attr_accessor 来存储current_rater 并从控制器传递 current_rate。

就像是:

#user.rb
attr_accessor :current_rater
def skill_ids=(ids)
  return false if current_rater.nil? || User.find_by_id(current_rater).nil?
  capabilities.where("skill_id not in (?)", ids).destroy_all
  ids.each do |skill_id|      
    capabilities.create(:skill_id => skill_id, :rater_id => self.current_rater) if capabilities.find_by_id(skill_id).nil?
  end
end

#users_controller.rb
def update
  @user = User.find(params[:id])

  #Replace 'current_user' with whatever method you are using to track the logged in user
  params[:user].merge(:current_rater => current_user) 

  respond_to do |format|
    ...
  end
end

可能没有您希望的那么优雅,但它应该可以完成这项工作吗?

于 2012-06-28T13:04:07.160 回答