Переопределение идентификатора при создании в ActiveRecord - PullRequest
101 голосов
/ 10 января 2009

Есть ли способ переопределить значение идентификатора модели при создании? Что-то вроде:

Post.create(:id => 10, :title => 'Test')

было бы идеально, но, очевидно, не будет работать.

Ответы [ 13 ]

116 голосов
/ 03 октября 2009

id это просто attr_protected, поэтому вы не можете использовать массовое назначение для его установки. Однако при ручной настройке он просто работает:

o = SomeObject.new
o.id = 8888
o.save!
o.reload.id # => 8888

Я не уверен, какова была первоначальная мотивация, но я делаю это при преобразовании моделей ActiveHash в ActiveRecord. ActiveHash позволяет вам использовать ту же семантику own_to в ActiveRecord, но вместо того, чтобы выполнять миграцию, создавать таблицу и нести накладные расходы на базу данных при каждом вызове, вы просто сохраняете свои данные в файлах yml. Внешние ключи в базе данных ссылаются на идентификаторы в памяти в yml.

ActiveHash отлично подходит для списков выбора и небольших таблиц, которые изменяются нечасто и меняются только разработчиками. Поэтому при переходе от ActiveHash к ActiveRecord проще всего сохранить все ссылки на внешние ключи одинаковыми.

29 голосов
/ 10 января 2009

Попробуйте

a_post = Post.new do |p| 
  p.id = 10
  p.title = 'Test'
  p.save
end

это должно дать вам то, что вы ищете.

27 голосов
/ 07 июня 2013

Вы также можете использовать что-то вроде этого:

Post.create({:id => 10, :title => 'Test'}, :without_protection => true)

Хотя, как указано в документах , это обойдет защиту массового назначения.

18 голосов
/ 10 сентября 2015

для рельсов 4:

Post.create(:title => 'Test').update_column(:id, 10)

Другие ответы по Rails 4 сработали не у меня. Многие из них казались измененными при проверке с помощью Rails Console, но когда я проверял значения в базе данных MySQL, они оставались неизменными. Другие ответы работали только иногда.

По крайней мере, для MySQL назначение id ниже номера идентификатора автоматического приращения не работает, если вы не используете update_column. Например,

p = Post.create(:title => 'Test')
p.id
=> 20 # 20 was the id the auto increment gave it

p2 = Post.create(:id => 40, :title => 'Test')
p2.id
=> 40 # 40 > the next auto increment id (21) so allow it

p3 = Post.create(:id => 10, :title => 'Test')
p3.id
=> 10 # Go check your database, it may say 41.
# Assigning an id to a number below the next auto generated id will not update the db

Если вы измените create на использование new + save, у вас все еще будет эта проблема. Ручное изменение id, например p.id = 10, также вызывает эту проблему.

В общем, я бы использовал update_column для изменения id, даже если это потребует дополнительного запроса к базе данных, потому что он будет работать постоянно. Это ошибка, которая может не отображаться в вашей среде разработки, но может незаметно повредить вашу производственную базу данных, говоря, что она работает.

6 голосов
/ 17 ноября 2010

мы можем переопределить attribute_protected_by_default

class Example < ActiveRecord::Base
    def self.attributes_protected_by_default
        # default is ["id", "type"]
        ["type"]
    end
end

e = Example.new(:id => 10000)
6 голосов
/ 13 января 2010

Как указывает Джефф, id ведет себя так, как будто это attr_protected. Чтобы предотвратить это, вам нужно переопределить список защищенных атрибутов по умолчанию. Будьте осторожны, делая это везде, где атрибутная информация может поступать извне. Поле id защищено по умолчанию для причины.

class Post < ActiveRecord::Base

  private

  def attributes_protected_by_default
    []
  end
end

(протестировано с ActiveRecord 2.3.5)

6 голосов
/ 31 марта 2009

На самом деле получается, что выполняет следующие работы:

p = Post.new(:id => 10, :title => 'Test')
p.save(false)
5 голосов
/ 22 марта 2013
Post.create!(:title => "Test") { |t| t.id = 10 }

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

post_types.each_with_index do |post_type|
  PostType.create!(:name => post_type) { |t| t.id = i + 1 }
end
2 голосов
/ 29 ноября 2012

Поместите эту create_with_id функцию вверху вашего seed.rb, а затем используйте ее для создания вашего объекта там, где желательны явные идентификаторы.

def create_with_id(clazz, params)
obj = clazz.send(:new, params)
obj.id = params[:id]
obj.save!
    obj
end

и используйте его вот так

create_with_id( Foo, {id:1,name:"My Foo",prop:"My other property"})

вместо использования

Foo.create({id:1,name:"My Foo",prop:"My other property"})

1 голос
/ 28 июля 2014

В этом случае аналогичная проблема, которая была необходима, перезаписать id с определенной датой:

# in app/models/calendar_block_group.rb
class CalendarBlockGroup < ActiveRecord::Base
...
 before_validation :parse_id

 def parse_id
    self.id = self.date.strftime('%d%m%Y')
 end
...
end

А потом:

CalendarBlockGroup.create!(:date => Date.today)
# => #<CalendarBlockGroup id: 27072014, date: "2014-07-27", created_at: "2014-07-27 20:41:49", updated_at: "2014-07-27 20:41:49">

Обратные вызовы работают нормально.

Удачи!.

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