Подтвердить что ты не робот

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() игнорировать атрибуты, которые не существуют в модели? В качестве альтернативы, как лучше всего удалить несуществующие атрибуты до создания новой записи?

Большое спасибо

4b9b3361

Ответ 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)))