Есть ли способ переопределить значение идентификатора модели при создании? Что-то вроде:
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 позволяет использовать ту же семантику own_to в ActiveRecord, но вместо того, чтобы выполнять миграцию и создавать таблицу и нести накладные расходы на базу данных при каждом вызове, вы просто сохраняете свои данные в файлах yml. Внешние ключи в базе данных ссылаются на идентификаторы в памяти в yml.
ActiveHash отлично подходит для раскрывающихся списков и небольших таблиц, которые меняются нечасто и изменяются только разработчиками. Поэтому при переходе от ActiveHash к ActiveRecord проще всего сохранить все ссылки на внешние ключи одинаковыми.
ActiveRecord::VERSION::STRING == "3.2.11"
здесь (с адаптером sqlite3), и все выше работает для меня.
Пытаться
a_post = Post.new do |p|
p.id = 10
p.title = 'Test'
p.save
end
это должно дать вам то, что вы ищете.
Вы также можете использовать что-то вроде этого:
Post.create({:id => 10, :title => 'Test'}, :without_protection => true)
Хотя, как указано в документации , это позволит обойти безопасность массового назначения.
Для Rails 4:
Post.create(:title => 'Test').update_column(:id, 10)
Другие ответы Rails 4 у меня не работали. Многие из них, казалось, изменились при проверке с помощью консоли Rails, но когда я проверил значения в базе данных 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
хотя это требует дополнительного запроса к базе данных, потому что он будет работать все время. Это ошибка, которая может не отображаться в вашей среде разработки, но может незаметно повредить вашу производственную базу данных, все время говоря, что она работает.
Post.new.update(id: 10, title: 'Test')
На самом деле получается, что делают следующие работы:
p = Post.new(:id => 10, :title => 'Test')
p.save(false)
validate: false
, а не просто 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 в начало файла 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"})
В этом случае возникла аналогичная проблема, когда необходимо было перезаписать 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">
Обратные вызовы работают нормально.
Удачи!.
id
метку времени на основе Unix. Я сделал это внутри before_create
. Работает отлично.
Для 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}`
{}
добавляя атрибуты, как ответ Самуэля выше (Rails3).
id
по-прежнему 10.
id
было передано как 10 - так что это именно то, что должно быть. Если это не то, чего вы ожидали, не могли бы вы пояснить это на примере?
вы можете вставить идентификатор по 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