Rails: игнорирование несуществующих атрибутов, переданных для создания()
У меня есть следующая модель Rails:
class CreateFoo < ActiveRecord::Migration
def self.up
create_table :foo do |t|
t.string :a
t.string :b
t.string :c
t.timestamps
end
end
def self.down
drop_table :foo
end
end
Если я попытаюсь создать новую запись с дополнительным несуществующим атрибутом, это приведет к ошибке:
Foo.create(a: 'some', b: 'string', c: 'foo', d: 'bar')
ActiveRecord::UnknownAttributeError: unknown attribute: d
Есть ли способ заставить create() игнорировать атрибуты, которые не существуют в модели? В качестве альтернативы, как лучше всего удалить несуществующие атрибуты до создания новой записи?
Большое спасибо
Ответы
Ответ 1
Попытка думать о потенциально более эффективном способе, но пока:
hash = { :a => 'some', :b => 'string', :c => 'foo', :d => 'bar' }
@something = Something.new
@something.attributes = hash.reject{|k,v| [email protected]?(k.to_s) }
@something.save
Ответ 2
Я использую это часто (упрощенно):
params.select!{|x| Model.attribute_names.index(x)}
Model.update_attributes(params)
Ответ 3
У меня была эта точная проблема с обновлением до Rails 3.2, когда я установил:
config.active_record.mass_assignment_sanitizer = :strict
Это вызвало некоторые из моих творений! вызовы для отказа, поскольку поля, которые ранее игнорировались, теперь вызывают ошибки массового присвоения. Я работал над этим, подделывая поля в модели следующим образом:
attr_accessor :field_to_exclude
attr_accessible :field_to_exclude
Ответ 4
Re: Есть ли способ заставить create() игнорировать атрибуты, которые не существуют в модели? - Нет, и это по дизайну.
Вы можете создать attr_setter, который будет использоваться create
-
attr_setter :a # will silently absorb additional parameter 'a' from the form.
Re: Альтернативно, как лучше всего удалить несуществующие атрибуты до создания новой записи?
Вы можете удалить их явно:
params[:Foo].delete(:a) # delete the extra param :a
Но лучше всего не ставить их там, в первую очередь. Измените форму, чтобы опустить их.
Добавлено:
Учитывая обновленную информацию (входящие данные), я думаю, что создаю новый хэш:
incoming_data_array.each{|rec|
Foo.create {:a => rec['a'], :b => rec['b'], :c => rec['c']} # create new
# rec from specific
# fields
}
Добавлено больше
# Another way:
keepers = ['a', 'b', 'c'] # fields used by the Foo class.
incoming_data_array.each{|rec|
Foo.create rec.delete_if{|key, value| !keepers.include?(key)} # create new rec
} # from kept
# fields
Ответ 5
Я придумал решение, которое выглядит так: вам может показаться полезным:
def self.create_from_hash(hash)
hash.select! {|k, v| self.column_names.include? k }
self.create(hash)
end
Это было идеальным решением для меня, потому что в моем случае hash
исходил из идеального источника данных, который отражал мою схему (кроме дополнительных полей).
Ответ 6
Я думаю, что использование метода attr_accessible в классе модели для Foo обеспечит то, что вы хотите, например:
class Foo < ActiveRecord::Base
attr_accessible :a, :b, :c
...
end
Это позволит установить/обновить только те атрибуты, которые перечислены в attr_accessible.
Ответ 7
Я нашел решение, которое работает достаточно хорошо, и, в конечном счете, представляет собой комбинацию указанных выше опций. Он допускает передачу недопустимых параметров (и игнорируется), а действительные - правильно отображаются объекту.
def self.initialize(params={})
User.new(params.reject { |k| !User.attribute_method?(k) })
end
Теперь вместо вызова User.new()
вызовите User.initialize()
. Это будет "фильтровать" правильные параметры довольно элегантно.
Ответ 8
Вы можете использовать метод Hash#slice
и column_names
также как метод класса.
hash = {a: 'some', b: 'string', c: 'foo', d: 'bar'}
Foo.create(hash.slice(*Foo.column_names.map(&:to_sym)))