Anulación de la identificación en create en ActiveRecord

¿Hay alguna forma de anular el valor de id de un modelo en create? Algo como:

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

sería ideal, pero obviamente no funcionará.

id está attr_protected, por lo que no puede usar la asignación masiva para configurarlo. Sin embargo, al configurarlo manualmente, simplemente funciona:

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

No estoy seguro de cuál fue la motivación original, pero lo hago al convertir los modelos de ActiveHash a ActiveRecord. ActiveHash le permite usar el mismo belongs_to semántica en ActiveRecord, pero en lugar de tener una migración y crear una tabla, y incurrir en la sobrecarga de la base de datos en cada llamada, solo almacena sus datos en archivos yml. Las claves externas en la base de datos hacen referencia a los identificadores en memoria en el yml.

ActiveHash es ideal para listas de selección y pequeñas tablas que cambian con poca frecuencia y solo cambian según los desarrolladores. Por lo tanto, al pasar de ActiveHash a ActiveRecord, es más fácil mantener todas las referencias de claves foráneas de la misma manera.

Tratar

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

eso debería darte lo que estás buscando.

También podrías usar algo como esto:

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

Aunque como se indica en los documentos , esto omitirá la seguridad de la asignación masiva.

Para Rails 4:

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

Otras respuestas de Rails 4 no funcionaron para mí. Muchos de ellos parecían cambiar al verificar usando Rails Console, pero cuando revisé los valores en la base de datos MySQL, permanecieron sin cambios. Otras respuestas solo funcionaban a veces.

Para MySQL al menos, asignar una id debajo del número de ID de incremento automático no funciona a menos que use update_column . Por ejemplo,

 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 

Si cambia la create para usar + + save , seguirá teniendo este problema. Cambiar manualmente la id como p.id = 10 también produce este problema.

En general, usaría update_column para cambiar la id aunque cueste una consulta de base de datos adicional porque funcionará todo el tiempo. Este es un error que puede no aparecer en su entorno de desarrollo, pero puede corromper silenciosamente su base de datos de producción todo el tiempo diciendo que está funcionando.

En realidad, resulta que hacer los siguientes trabajos:

 p = Post.new(:id => 10, :title => 'Test') p.save(false) 

Como señala Jeff, id se comporta como si estuviera attr_protected. Para evitar eso, debe sobrescribir la lista de atributos protegidos predeterminados. Tenga cuidado al hacer esto en cualquier lugar donde la información de atributos pueda venir del exterior. El campo de id. Está predeterminado protegido por un motivo.

 class Post < ActiveRecord::Base private def attributes_protected_by_default [] end end 

(Probado con ActiveRecord 2.3.5)

podemos anular atributos_protegidos_por_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 } 

Esto no me parece el tipo de cosas que normalmente desearía hacer, pero funciona bastante bien si necesita llenar una tabla con un conjunto fijo de identificadores (por ejemplo al crear valores predeterminados usando una tarea de rake) y usted desea anular la autoincrementación (para que cada vez que ejecute la tarea, la tabla se llene con los mismos ID):

 post_types.each_with_index do |post_type| PostType.create!(:name => post_type) { |t| t.id = i + 1 } end 

Ponga esta función create_with_id en la parte superior de su seeds.rb y luego úsela para crear sus objetos donde se desean identificadores explícitos.

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

y úsalo así

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

En lugar de usar

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

Este caso es un problema similar que fue necesario sobrescribir el id con una especie de fecha personalizada:

 # 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 

Y entonces :

 CalendarBlockGroup.create!(:date => Date.today) # => # 

Las rellamadas funcionan bien.

¡Buena suerte!.

Para Rails 3, la forma más sencilla de hacerlo es usar new con el refinamiento sin without_protection , y luego save :

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

Para los datos de inicialización, puede tener sentido omitir la validación, que puede hacer así:

 Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save(validate: false) 

De hecho, hemos agregado un método de ayuda a ActiveRecord :: Base que se declara inmediatamente antes de ejecutar los archivos semilla:

 class ActiveRecord::Base def self.seed_create(attributes) new(attributes, without_protection: true).save(validate: false) end end 

Y ahora:

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

Para Rails 4, debe usar StrongParams en lugar de atributos protegidos. Si este es el caso, simplemente podrá asignar y guardar sin pasar ninguna bandera a la new :

 Post.new(id: 10, title: 'Test').save # optionally pass `{validate: false}` 

En Rails 4.2.1 con Postgresql 9.5.3, Post.create(:id => 10, :title => 'Test') funciona siempre que no haya una fila con id = 10.

puedes insertar id por 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