Есть ли способ переопределить значение идентификатора модели для создания? Что-то вроде:
Post.create(:id => 10, :title => 'Test')
будет идеальным, но, очевидно, не будет работать.
Есть ли способ переопределить значение идентификатора модели для создания? Что-то вроде:
Post.create(:id => 10, :title => 'Test')
будет идеальным, но, очевидно, не будет работать.
id просто attr_protected, поэтому вы не можете использовать массовое присвоение для его установки. Однако при настройке вручную оно работает:
o = SomeObject.new
o.id = 8888
o.save!
o.reload.id # => 8888
Я не уверен, какова была первоначальная мотивация, но я делаю это при преобразовании моделей ActiveHash в ActiveRecord. ActiveHash позволяет использовать те же атрибуты belongs_to для семантики в ActiveRecord, но вместо миграции и создания таблицы, а также из-за накладных расходов на базу данных при каждом вызове вы просто сохраняете свои данные в yml файлах. Внешние ключи в базе данных ссылаются на идентификаторы in-memory в yml.
ActiveHash отлично подходит для picklists и небольших таблиц, которые редко меняются и меняются только разработчиками. Поэтому при переходе с ActiveHash на ActiveRecord проще всего сохранить все ссылки на внешние ключи.
Попробуйте
a_post = Post.new do |p|
p.id = 10
p.title = 'Test'
p.save
end
который должен дать вам то, что вы ищете.
Вы также можете использовать что-то вроде этого:
Post.create({:id => 10, :title => 'Test'}, :without_protection => true)
Хотя, как указано в docs, это обходит защиту массового присвоения.
Для Rails 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
, даже если он требует дополнительного запроса к базе данных, потому что он будет работать все время. Это ошибка, которая может не отображаться в вашей среде разработки, но может спокойно испортить вашу производственную базу данных, показывая, что она работает.
На самом деле получается, что выполнение следующих работ:
p = Post.new(:id => 10, :title => 'Test')
p.save(false)
Как указывает Джефф, id ведет себя так, как будто attr_protected. Чтобы этого не произошло, вам необходимо переопределить список защищенных по умолчанию атрибутов. Будьте осторожны, делая это везде, где информация об атрибутах может поступать извне. Поле id по умолчанию защищено по какой-либо причине.
class Post < ActiveRecord::Base
private
def attributes_protected_by_default
[]
end
end
(Протестировано с помощью ActiveRecord 2.3.5)
мы можем переопределить attributes_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)
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
Поместите эту функцию create_with_id вверху вашего seeds.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"})
Этот случай является аналогичной проблемой, которая была необходима для перезаписывания 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">
Обратные вызовы работают нормально.
Удачи!
В Rails 4.2.1 с Postgresql 9.5.3, Post.create(:id => 10, :title => 'Test')
работает до тех пор, пока не будет строки с id = 10.
Для Rails 3 самый простой способ сделать это - использовать new
с уточнением without_protection
, а затем save
:
Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save
Для данных семени может иметь смысл обходить проверку, которую вы можете сделать следующим образом:
Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save(validate: false)
Фактически мы добавили вспомогательный метод в ActiveRecord:: Base, который объявляется непосредственно перед выполнением файлов семян:
class ActiveRecord::Base
def self.seed_create(attributes)
new(attributes, without_protection: true).save(validate: false)
end
end
И теперь:
Post.seed_create(:id => 10, :title => 'Test')
Для Rails 4 вы должны использовать StrongParams вместо защищенных атрибутов. Если это так, вы просто сможете назначить и сохранить без передачи каких-либо флагов в new
:
Post.new(id: 10, title: 'Test').save # optionally pass `{validate: false}`
вы можете вставить id по sql:
arr = record_line.strip.split(",")
sql = "insert into records(id, created_at, updated_at, count, type_id, cycle, date) values(#{arr[0]},#{arr[1]},#{arr[2]},#{arr[3]},#{arr[4]},#{arr[5]},#{arr[6]})"
ActiveRecord::Base.connection.execute sql