Rails-如何保护外键并允许关联选择



为了简单起见,假设我有一个简单的具有多个贯穿关系的

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对象(传入:user:course,但不冒稍后恶意更新这些外键的风险?

  2. 我该如何设置我的验证,使两个belongs_to关联都是必需的,但也允许在嵌套表单中创建注册对象?

第一个问题的答案

一种解决方案是可以将:user: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_usersafe_course字段,而不是usercourse。在这种情况下,它只会设置实际的usercourse,如果它们是空的,并且它们实际上从未公开为可访问的。您可以通过使用回调条件来绕过此规则。

第二个问题的答案

当你以复杂的形式提交一堆数据时,你应该想出一种方法来判断这些数据中哪些是有意义的,哪些只是占位符。如果没有提交有意义的数据,就不要创建注册。你必须决定哪些表单字段"足够",才能看到有人真的试图填写它们并犯了错误,而不是有人从未接触过嵌套表单。如果没有简单的方法来确定这一点,一种方法是在嵌套表单中添加一个"添加此注册"复选框。如果选中该复选框,将尝试创建并运行验证。之后,您可以添加一些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' }

有了reject_if,它会告诉rails,如果给定proc中的条件不满足,它将忽略注册,而不是试图创建和验证它

希望这能给你一些想法。

最新更新