¿Hay alguna forma de anular el valor de identificación de un modelo en la creación? Algo como:
Post.create(:id => 10, :title => 'Test')
sería ideal, pero obviamente no funcionará.
¿Hay alguna forma de anular el valor de identificación de un modelo en la creación? Algo como:
Post.create(:id => 10, :title => 'Test')
sería ideal, pero obviamente no funcionará.
Respuestas:
id es solo attr_protected, por lo que no puede usar 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 hago esto cuando convierto modelos de ActiveHash a ActiveRecord. ActiveHash le permite usar la misma semántica pertenece_ a en ActiveRecord, pero en lugar de tener una migración y crear una tabla, e incurrir en la sobrecarga de la base de datos en cada llamada, simplemente almacena sus datos en archivos yml. Las claves externas en la base de datos hacen referencia a los identificadores en memoria en el archivo yml.
ActiveHash es ideal para listas de selección y tablas pequeñas que cambian con poca frecuencia y solo lo cambian los desarrolladores. Entonces, cuando se pasa de ActiveHash a ActiveRecord, es más fácil mantener todas las referencias de claves externas iguales.
ActiveRecord::VERSION::STRING == "3.2.11"
aquí (con el adaptador sqlite3) y lo anterior funciona para mí.
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 evitará la seguridad de asignación masiva.
Para rieles 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 la consola Rails, pero cuando verifiqué los valores en la base de datos MySQL, permanecieron sin cambios. Otras respuestas solo funcionaron a veces.
Para MySQL al menos, la asignación de un id
número de identificación por debajo del 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 create
para usar new
+ save
, seguirá teniendo este problema. Cambiar manualmente algo id
similar p.id = 10
también produce este problema.
En general, usaría update_column
para cambiar el id
aunque cuesta 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 mientras dice que está funcionando.
Post.new.update(id: 10, title: 'Test')
En realidad, resulta que hacer lo siguiente funciona:
p = Post.new(:id => 10, :title => 'Test')
p.save(false)
validate: false
, en lugar de simplemente false
. Sin embargo, aún se encuentra con el problema del atributo protegido: hay una forma separada de lo que he descrito en mi respuesta.
Como señala Jeff, id se comporta como si estuviera attr_protected. Para evitarlo, debe anular la lista de atributos protegidos predeterminados. Tenga cuidado al hacer esto en cualquier lugar donde la información de atributos pueda provenir del exterior. El campo de identificación está protegido por defecto por una razón.
class Post < ActiveRecord::Base
private
def attributes_protected_by_default
[]
end
end
(Probado con ActiveRecord 2.3.5)
podemos anular atributos_protegido_por_defecto
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 completar una tabla con un conjunto fijo de identificadores (por ejemplo, al crear valores predeterminados usando una tarea de rake) y usted desea anular el incremento automático (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 hacer su creación de 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 sobrescribirlo 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)
# => #<CalendarBlockGroup id: 27072014, date: "2014-07-27", created_at: "2014-07-27 20:41:49", updated_at: "2014-07-27 20:41:49">
Las devoluciones de llamada funcionan bien.
¡Buena suerte!.
id
marca de tiempo basada en Unix. Lo he hecho por dentro before_create
. Funciona bien.
Para Rails 3, la forma más sencilla de hacer esto es usar new
con el without_protection
refinamiento 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, lo que puede hacer así:
Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save(validate: false)
De hecho, hemos agregado un método auxiliar a ActiveRecord :: Base que se declara inmediatamente antes de ejecutar 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, debería utilizar StrongParams en lugar de atributos protegidos. Si este es el caso, simplemente podrá asignar y guardar sin pasar ninguna marca a new
:
Post.new(id: 10, title: 'Test').save # optionally pass `{validate: false}`
{}
la respuesta de Samuel anterior (Rails3).
id
sigue siendo 10.
id
se pasó como 10, así que eso es exactamente lo que debería ser. Si eso no es lo que esperabas, ¿puedes aclarar con un ejemplo?