Rails: игнорирование несуществующих атрибутов, переданных в create () - PullRequest
26 голосов
/ 09 ноября 2010

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

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

Ответы [ 8 ]

37 голосов
/ 09 ноября 2010

Попытка придумать потенциально более эффективный способ, но пока:

hash = { :a => 'some', :b => 'string', :c => 'foo', :d => 'bar' }
@something = Something.new
@something.attributes = hash.reject{|k,v| !@something.attributes.keys.member?(k.to_s) }
@something.save
10 голосов
/ 20 декабря 2012

Я часто использую это (упрощенно):

params.select!{|x| Model.attribute_names.index(x)}
Model.update_attributes(params)
7 голосов
/ 14 февраля 2012

У меня была именно эта проблема при обновлении до Rails 3.2, когда я установил:

config.active_record.mass_assignment_sanitizer = :strict

Это вызвало некоторые мои творения! вызывает сбой, поскольку поля, которые ранее игнорировались, теперь вызывают массовые ошибки присваивания. Я обошел это, подделав поля в модели следующим образом:

attr_accessor   :field_to_exclude
attr_accessible :field_to_exclude
6 голосов
/ 09 ноября 2010

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
3 голосов
/ 03 сентября 2014

Я пришел к решению, которое выглядит следующим образом, вы можете найти его полезным:

def self.create_from_hash(hash)
  hash.select! {|k, v| self.column_names.include? k }
  self.create(hash)
end

Это было идеальное решение для меня, потому что в моем случае hash исходило из идеальных данныхисточник, который отражал мою схему (кроме дополнительных полей).

1 голос
/ 06 июля 2011

Я думаю, что использование метода attr_accessible в классе модели для Foo позволит достичь того, что вы хотите, например:

class Foo < ActiveRecord::Base

  attr_accessible :a, :b, :c

  ...
end

Это позволит установить / обновить только эти атрибутыперечислено с attr_accessible .

0 голосов
/ 12 января 2017

Вы можете использовать Hash#slice и column_names метод, также существует как метод класса.

hash = {a: 'some', b: 'string', c: 'foo', d: 'bar'}
Foo.create(hash.slice(*Foo.column_names.map(&:to_sym)))
0 голосов
/ 25 февраля 2015

Я нашел решение, которое работает довольно хорошо и в конечном итоге представляет собой комбинацию из перечисленных выше вариантов.Он позволяет передавать (и игнорировать) недопустимые параметры, в то время как действительные параметры правильно отображаются на объект.

def self.initialize(params={})
  User.new(params.reject { |k| !User.attribute_method?(k) })
end

Теперь вместо вызова User.new() вызовите User.initialize().Это «элегантно» отфильтрует правильные параметры.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...