Sequel::Model
Mass Assignment¶ ↑
Most Model methods that take a hash of attribute keys and values, including Model.new
, Model.create
, Model#set
and Model#update
are subject to Sequel’s mass assignment rules.
If you have an instance of a plain Sequel::Model
class:
class Post < Sequel::Model end post = Post.new
and you call a mass assignment method with a hash:
post.set(title: 'T', body: 'B')
the mass assignment method will go through each key in the hash, append =
to it to determine the setter method, and if the setter method is defined and access to it is not restricted, Sequel
will call the setter method with the hash value. So if we assume that the posts table has title and body columns, what the above mass assignment call actually does is:
post.title=('T') post.body=('B')
By default, there are two types of setter methods that are restricted. The first is methods like typecast_on_assignment=
and ==
, which don’t affect columns. These methods cannot be enabled for mass assignment. The second is primary key setters.
So if you do:
post = Post.new(id: 1)
Sequel
will raise a Sequel::MassAssignmentRestriction
exception, since by default setting the primary key is not allowed.
To enable use of primary key setters, you need to call unrestrict_primary_key
for that model:
Post.unrestrict_primary_key
If you want to change mass assignment so it ignores attempts to access restricted setter methods, you can do:
# Global default Sequel::Model.strict_param_setting = false # Class level Post.strict_param_setting = false # Instance level post.strict_param_setting = false
Since mass assignment by default allows modification of all column values except for primary key columns, it can be a security risk in some cases. If you are dealing with untrusted input, you are generally going to want to restrict what should be updated.
Sequel
has Model#set_fields
and Model#update_fields
methods, which are designed to be used with untrusted input. These methods take two arguments, the untrusted hash as the first argument, and a trusted array of field names as the second argument:
post.set_fields({title: 'T', body: 'B'}, [:title, :body])
Instead of looking at every key in the untrusted hash, set_fields
will iterate over the trusted field names, looking each up in the hash, and calling the setter method appropriately with the result. set_fields
basically translates the above method call to:
post.title=('T') post.body=('B')
By using this method, you can be sure that the mass assignment method only sets the fields you expect it to set.
Note that if one of the fields does not exist in the hash:
post.set_fields({title: 'T'}, [:title, :body])
set_fields
will set the value to nil (the default hash value) by default, with behavior equivalent to:
post.title=('T') post.body=(nil)
You can use the :missing option to set_fields
to change the behavior:
post.set_fields({title: 'T'}, [:title, :body], missing: :skip) # post.title=('T') # only post.set_fields({title: 'T'}, [:title, :body], missing: :raise) # raises Sequel::Error
If you want to set a model level default for the set_fields
options, you can use the default_set_fields_options
class accessor:
# Global default Sequel::Model.default_set_fields_options[:missing] = :skip # Class level Post.default_set_fields_options[:missing] = :skip
Here’s a table describing Sequel’s default mass assignment methods:
Model.new(hash) |
Creates a new model instance, then calls Model#set(hash) |
Model.create(hash) |
Calls Model.new(hash).save |
Model#set(hash) |
Calls related setter method (unless access is restricted) for each key in the hash, then returns self |
Model#update(hash) |
Calls set(hash).save_changes |
Model#set_fields(hash, columns, options) |
For each column in columns, looks up related entry in hash, and calls the related setter method |
Model#update_fields(hash, columns, options) |
Calls set_fields(hash, columns, options).save_changes |
For backwards compatibility, Sequel
also ships with a whitelist_security and blacklist_security plugins that offer additional mass assignment methods, but it is recommended to use set_fields
or update_fields
for untrusted input, and the other methods for trusted input.