C'è un modo per sovrascrivere il valore dell'ID di un modello durante la creazione? Qualcosa di simile a:
Post.create(:id => 10, :title => 'Test')
sarebbe l'ideale, ma ovviamente non funzionerà.
C'è un modo per sovrascrivere il valore dell'ID di un modello durante la creazione? Qualcosa di simile a:
Post.create(:id => 10, :title => 'Test')
sarebbe l'ideale, ma ovviamente non funzionerà.
Risposte:
id è solo attr_protected, motivo per cui non è possibile utilizzare l'assegnazione di massa per impostarlo. Tuttavia, quando lo si imposta manualmente, funziona semplicemente:
o = SomeObject.new
o.id = 8888
o.save!
o.reload.id # => 8888
Non sono sicuro di quale fosse la motivazione originale, ma lo faccio quando converto i modelli ActiveHash in ActiveRecord. ActiveHash ti consente di utilizzare la stessa semantica appartiene_to in ActiveRecord, ma invece di avere una migrazione e creare una tabella e incorrere nel sovraccarico del database a ogni chiamata, devi semplicemente memorizzare i tuoi dati in file yml. Le chiavi esterne nel database fanno riferimento agli ID in memoria nel file yml.
ActiveHash è ottimo per elenchi di selezione e tabelle piccole che cambiano di rado e cambiano solo dagli sviluppatori. Quindi, quando si passa da ActiveHash ad ActiveRecord, è più semplice mantenere uguali tutti i riferimenti alla chiave esterna.
ActiveRecord::VERSION::STRING == "3.2.11"
qui (con l'adattatore sqlite3) e quanto sopra funziona per me.
Provare
a_post = Post.new do |p|
p.id = 10
p.title = 'Test'
p.save
end
questo dovrebbe darti quello che stai cercando.
Potresti anche usare qualcosa del genere:
Post.create({:id => 10, :title => 'Test'}, :without_protection => true)
Sebbene, come affermato nei documenti , ciò aggirerà la sicurezza dell'assegnazione di massa.
Per Rails 4:
Post.create(:title => 'Test').update_column(:id, 10)
Altre risposte di Rails 4 non hanno funzionato per me. Molti di loro sembravano cambiare durante il controllo usando la Rails Console, ma quando ho controllato i valori nel database MySQL, sono rimasti invariati. Altre risposte funzionavano solo a volte.
Almeno per MySQL, l'assegnazione di un id
sotto il numero ID di incremento automatico non funziona a meno che non si utilizzi update_column
. Per esempio,
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
Se cambi create
per usare new
+ save
avrai ancora questo problema. Anche la modifica manuale di id
simili p.id = 10
produce questo problema.
In generale, lo userei update_column
per cambiare id
anche se costa una query di database extra perché funzionerà tutto il tempo. Questo è un errore che potrebbe non essere visualizzato nel tuo ambiente di sviluppo, ma può danneggiare silenziosamente il tuo database di produzione per tutto il tempo dicendo che funziona.
Post.new.update(id: 10, title: 'Test')
In realtà, si scopre che facendo i seguenti lavori:
p = Post.new(:id => 10, :title => 'Test')
p.save(false)
validate: false
, invece che semplicemente false
. Tuttavia, ti imbatti ancora nel problema degli attributi protetti: c'è un modo separato per aggirare quello che ho delineato nella mia risposta.
Come sottolinea Jeff, id si comporta come se fosse attr_protected. Per evitare ciò, è necessario sovrascrivere l'elenco degli attributi protetti predefiniti. Fai attenzione a farlo ovunque le informazioni sugli attributi possano provenire dall'esterno. Il campo id è protetto per impostazione predefinita per un motivo.
class Post < ActiveRecord::Base
private
def attributes_protected_by_default
[]
end
end
(Testato con ActiveRecord 2.3.5)
possiamo eseguire l'override di 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 }
Questo non mi colpisce come il genere di cose che normalmente vorresti fare, ma funziona abbastanza bene se devi popolare una tabella con un set fisso di ID (ad esempio quando crei valori predefiniti usando un'attività rake) e tu desidera sovrascrivere l'incremento automatico (in modo che ogni volta che si esegue l'attività la tabella venga popolata con gli stessi ID):
post_types.each_with_index do |post_type|
PostType.create!(:name => post_type) { |t| t.id = i + 1 }
end
Metti questa funzione create_with_id nella parte superiore del tuo seed.rb e poi usala per creare il tuo oggetto dove sono desiderati gli id espliciti.
def create_with_id(clazz, params)
obj = clazz.send(:new, params)
obj.id = params[:id]
obj.save!
obj
end
e usalo in questo modo
create_with_id( Foo, {id:1,name:"My Foo",prop:"My other property"})
invece di usare
Foo.create({id:1,name:"My Foo",prop:"My other property"})
Questo caso è un problema simile che è stato necessario sovrascrivere id
con una sorta di data personalizzata:
# 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
E poi :
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">
I callback funzionano bene.
In bocca al lupo!.
id
timestamp basato su Unix. L'ho fatto dentro before_create
. Funziona bene.
Per Rails 3, il modo più semplice per farlo è usare new
con il without_protection
raffinamento, quindi save
:
Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save
Per i dati seed, potrebbe avere senso aggirare la convalida che puoi fare in questo modo:
Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save(validate: false)
In realtà abbiamo aggiunto un metodo di supporto ad ActiveRecord :: Base che viene dichiarato immediatamente prima dell'esecuzione dei file seed:
class ActiveRecord::Base
def self.seed_create(attributes)
new(attributes, without_protection: true).save(validate: false)
end
end
E adesso:
Post.seed_create(:id => 10, :title => 'Test')
Per Rails 4, dovresti usare StrongParams invece di attributi protetti. Se questo è il caso, sarai semplicemente in grado di assegnare e salvare senza passare alcun flag a new
:
Post.new(id: 10, title: 'Test').save # optionally pass `{validate: false}`
{}
come risposta di Samuel sopra (Rails3).
id
sono ancora 10.
id
stato passato come 10, quindi è esattamente quello che dovrebbe essere. Se non è quello che ti aspettavi, puoi chiarire con un esempio?