Есть ли способ переопределить значение идентификатора модели при создании? Что-то вроде:
Post.create(:id => 10, :title => 'Test')
было бы идеально, но явно не сработает.





Пытаться
a_post = Post.new do |p|
p.id = 10
p.title = 'Test'
p.save
end
это должно дать вам то, что вы ищете.
не уверен, почему вас отвергают, это отлично работает для меня
Это продолжает работать с activerecord 3.2.11. Ответ, отправленный Джеффом Дином 2 октября 2009 года, больше не работает.
не работает, похоже, работает только потому, что вызывает p.save, который, вероятно, возвращает false. получит ActiveRecord :: RecordNotFound, если вы запустили p.save!
Собственно получается, что делают следующие работы:
p = Post.new(:id => 10, :title => 'Test')
p.save(false)
Хотя это может работать, он также отключает все проверки, что может быть не тем, что предполагалось.
Это именно то, что мне нужно для исходных данных, где важны идентификаторы. Спасибо.
Первоначально я проголосовал за, думая, что это сработает для исходных данных, как указал @JD, но затем я попробовал это с помощью activerecord 3.2.13, и я все еще получаю ошибку «Невозможно назначить защищенные атрибуты». Итак, проголосовали против :(
К сожалению, это не работает в rails 4, вы получаете метод NoMethodError: undefined `[] 'для false: FalseClass
@JorgeSampayo Это все еще работает, если вы передадите validate: false, а не просто false. Однако вы по-прежнему сталкиваетесь с проблемой защищенного атрибута - есть другой способ обойти то, что я изложил в своем ответе.
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 проще всего сохранить все ссылки на внешние ключи одинаковыми.
@jkndrkn - Я не понимаю, о чем ты. У меня здесь ActiveRecord::VERSION::STRING == "3.2.11" (с адаптером sqlite3), и все вышесказанное у меня работает.
Возможно, то, что я испытал, выразилось только с драйвером mysql.
@jkndrkn У меня работает с драйвером MySQL для Rails 3.2.18
работал у меня. спас мою жизнь. используется на рельсах 4.0.0 / postgres 9.3.5
См. мой ответ, чтобы узнать, как это сделать в Rails 4.
Как указывает Джефф, 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)
Поместите эту функцию 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"})
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
Вы также можете использовать что-то вроде этого:
Post.create({:id => 10, :title => 'Test'}, :without_protection => true)
Хотя, как указано в документы, это позволит обойти безопасность массового назначения.
Хорошая находка, @Samuel Heaney, я могу убедиться, что это отлично работает с activerecord 3.2.13.
У меня тоже сработало; не нужно было включать отдельное поле.
Увы, на Rails 4 это больше не работает, они убрали хеш опций
@JorgeSampayo - в Rails 4 вам это не нужно, поскольку защищенные атрибуты можно удалить в пользу StrongParams.
В этом случае возникла аналогичная проблема, когда необходимо было перезаписать 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).
@PinnyM Ваш ответ Rails 4 не работает для меня. id по-прежнему 10.
@RickSmith в данном примере id был передан как 10 - так что это именно то, что должно быть. Если это не то, чего вы ожидали, не могли бы вы пояснить это на примере?
Извините, я все еще хотел сказать нет 10. См. Мой ответ для объяснения.
@RickSmith - интересно, эта проблема присуща только MySQL? В любом случае, обычно первичные ключи назначаются напрямую для начальных данных. В таком случае вам, как правило, НЕ следует пытаться вводить значения ниже порогового значения автоинкремента, или вам следует отключить автоинкремент для этого набора команд.
@PinnyM Не уверен, является ли он уникальным для MySQL. Теоретически вы правы в отношении исходных данных, но моя внутренняя паранойя все время говорит мне, что если есть какой-то вариант использования, о котором я не думал, он укусит меня как можно хуже. Может у меня посттравматический стресс?
Для 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, даже если это требует дополнительного запроса к базе данных, потому что он будет работать все время. Это ошибка, которая может не отображаться в вашей среде разработки, но может незаметно повредить вашу производственную базу данных, все время говоря, что она работает.
Это также был единственный способ заставить его работать в ситуации rails 3.2.22 в консоли. Ответы с вариантами «сохранить» не повлияли.
Для рельсов 4+ использую: Post.new.update(id: 10, title: 'Test')
В Rails 4.2.1 с Postgresql 9.5.3 Post.create(:id => 10, :title => 'Test') работает до тех пор, пока еще нет строки с 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
Многие из этих ответов периодически терпят неудачу в Rails 4 и говорят, что они работают. См. Объяснение в мой ответ.