2

为简单起见,假设我有一个简单的 has-many-through 关系

class User < ActiveRecord::Base
  has_many :courses, :through => :registrations
end

class Registration < ActiveRecord::Base
  belongs_to :user
  belongs_to :course
end

class Course < ActiveRecord::Base
  has_many :users, :through => :registrations
end

我想保证我的应用程序安全,所以我attr_accessible习惯将我的属性列入白名单。

我的问题是双重的:

  1. 我将如何设置我的白名单属性,以便我可以通过表单创建一个新的 Registration 对象(传入:userand :course,但不会冒险允许这些外键稍后被恶意更新?

  2. 我将如何设置我的验证,以便belongs_to需要两个关联但也允许在嵌套表单中创建注册对象?

4

1 回答 1

2

回答第一个问题

一种解决方案是您可以将:userand标记:course为只读。

attr_readonly :user_id, :course_id

这样就可以在创建时设置它们,但不能在更新时设置它们。如果您希望它们将来可以更新,您可以在注册模型上创建一个特殊的方法,@registration.update_dangerous_stuff(params)并且只在具有更高权限的用户可以访问的控制器操作中使用此方法。该方法将使用类似的东西update_column来改变这些字段。

另一种方法是创建用于设置用户和课程的虚拟属性,这些属性具有决定是否可以设置用户或课程的逻辑。这是一个例子。

attr_accessor :safe_user, :safe_course
attr_accessible :safe_user, :safe_course


before_save :set_user, if: 'user.blank?'
before_save :set_course, if: 'course.blank?'


def set_user
  self.user = safe_user
end

def set_course
  self.course = safe_course
end

因此,在您将使用safe_userandsafe_course字段而不是userand的表单中course。在这种情况下,它只会设置实际的user并且course如果它们是空白的,并且它们实际上从未公开为可访问的。您可以通过使用回调条件来绕过此规则。

回答第二个问题

当您以复杂的形式提交一堆数据时,您需要想出一种方法来判断这些数据中哪些是有意义的,哪些只是一个占位符。如果没有提交有意义的数据,就不要创建注册。您必须决定哪些表单字段“足以”看到有人实际上尝试填写它们并犯了错误,而不是有人甚至从未接触过嵌套表单的情况。如果没有简单的方法来确定 - 一种方法是在嵌套表单中添加一个复选框“添加此注册”。如果选中该复选框 - 将尝试创建并运行验证。之后,您可以添加一些 javascript 来隐藏此复选框,并在有人激活嵌套表单中的任何字段时自动检查它。

为了在后端促进这种行为,你有accepts_nested_attributes_for rails 方法。例如,您可以在课程模型中说如下内容。

accepts_nested_attributes_for :registrations,
  reject_if: -> attrs { attrs[:name].blank? }

# Don't forget this too 
attr_accessible :registrations_attributes

Rails 还提供了一个快捷方式。

accepts_nested_attributes_for :registrations, reject_if: :all_blank

在复选框的情况下,您可以说reject_if: -> attrs { attrs[:my_check_box] == '1' },等等。

告诉railsreject_if如果不满足给定过程中的条件,它将简单地忽略注册,而不是尝试创建和验证它。

希望这能给你一些想法。

于 2012-09-15T06:07:22.247 回答