ruby on rails - form_with - Anulación de la identificación en create en ActiveRecord
select rails helper (13)
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)
¿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á.
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.
En realidad, resulta que hacer los siguientes trabajos:
p = Post.new(:id => 10, :title => ''Test'')
p.save(false)
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)
# => #<CalendarBlockGroup id: 27072014, date: "2014-07-27", created_at: "2014-07-27 20:41:49", updated_at: "2014-07-27 20:41:49">
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}`
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.
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"})
También podrías usar algo como esto:
Post.create({:id => 10, :title => ''Test''}, :without_protection => true)
Aunque como se indica en los docs , esto omitirá la seguridad de la asignación masiva.
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.
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.
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)
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
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