Existe alguma maneira de substituir o valor de id de um modelo na criação? Algo como:
Post.create(:id => 10, :title => 'Test')
seria ideal, mas obviamente não funcionará.
Existe alguma maneira de substituir o valor de id de um modelo na criação? Algo como:
Post.create(:id => 10, :title => 'Test')
seria ideal, mas obviamente não funcionará.
Respostas:
id é apenas attr_protected, é por isso que você não pode usar atribuição em massa para defini-lo. No entanto, ao configurá-lo manualmente, ele simplesmente funciona:
o = SomeObject.new
o.id = 8888
o.save!
o.reload.id # => 8888
Não tenho certeza de qual foi a motivação original, mas faço isso ao converter modelos ActiveHash para ActiveRecord. ActiveHash permite que você use a mesma semântica belongs_to no ActiveRecord, mas ao invés de ter uma migração e criar uma tabela, e incorrer na sobrecarga do banco de dados em cada chamada, você apenas armazena seus dados em arquivos yml. As chaves estrangeiras no banco de dados fazem referência aos ids na memória no yml.
ActiveHash é ótimo para listas de opções e pequenas tabelas que mudam com pouca frequência e apenas mudam pelos desenvolvedores. Portanto, ao passar de ActiveHash para ActiveRecord, é mais fácil apenas manter todas as referências de chave estrangeira iguais.
ActiveRecord::VERSION::STRING == "3.2.11"
aqui (com o adaptador sqlite3) e o acima funciona para mim.
Experimentar
a_post = Post.new do |p|
p.id = 10
p.title = 'Test'
p.save
end
isso deve dar a você o que você está procurando.
Você também pode usar algo assim:
Post.create({:id => 10, :title => 'Test'}, :without_protection => true)
Embora conforme declarado nos documentos , isso contornará a segurança de atribuição em massa.
Para Rails 4:
Post.create(:title => 'Test').update_column(:id, 10)
Outras respostas do Rails 4 não funcionaram para mim. Muitos deles pareceram mudar durante a verificação usando o Console do Rails, mas quando verifiquei os valores no banco de dados MySQL, eles permaneceram inalterados. Outras respostas só funcionaram algumas vezes.
Para MySQL, pelo menos, atribuir um id
número de id abaixo do auto incremento não funciona a menos que você use update_column
. Por exemplo,
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 você mudar create
para usar new
+ save
, ainda terá esse problema. Alterar manualmente o id
semelhante p.id = 10
também produz esse problema.
Em geral, eu usaria update_column
para alterar o, id
mesmo que custe uma consulta de banco de dados extra porque funcionará o tempo todo. Este é um erro que pode não aparecer em seu ambiente de desenvolvimento, mas pode corromper silenciosamente seu banco de dados de produção enquanto diz que está funcionando.
Post.new.update(id: 10, title: 'Test')
Na verdade, acontece que fazer o seguinte funciona:
p = Post.new(:id => 10, :title => 'Test')
p.save(false)
validate: false
, ao invés de simplesmente false
. No entanto, você ainda se depara com o problema do atributo protegido - há uma maneira separada de contornar o que descrevi em minha resposta.
Como Jeff aponta, id se comporta como se fosse attr_protected. Para evitar isso, você precisa substituir a lista de atributos protegidos padrão. Tenha cuidado ao fazer isso em qualquer lugar onde as informações do atributo possam vir de fora. O campo id é protegido por padrão por um motivo.
class Post < ActiveRecord::Base
private
def attributes_protected_by_default
[]
end
end
(Testado com ActiveRecord 2.3.5)
podemos substituir attribute_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 }
Isso não me parece o tipo de coisa que você normalmente gostaria de fazer, mas funciona muito bem se você precisar preencher uma tabela com um conjunto fixo de ids (por exemplo, ao criar padrões usando uma tarefa rake) e você deseja substituir o incremento automático (de modo que cada vez que você executar a tarefa, a tabela seja preenchida com os mesmos IDs):
post_types.each_with_index do |post_type|
PostType.create!(:name => post_type) { |t| t.id = i + 1 }
end
Coloque esta função create_with_id no topo de seu seed.rb e então use-a para fazer sua criação de objeto onde ids explícitos são desejados.
def create_with_id(clazz, params)
obj = clazz.send(:new, params)
obj.id = params[:id]
obj.save!
obj
end
e usar assim
create_with_id( Foo, {id:1,name:"My Foo",prop:"My other property"})
ao invés de usar
Foo.create({id:1,name:"My Foo",prop:"My other property"})
Este caso é um problema semelhante que foi necessário substituir o id
por um tipo de data 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
E depois :
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">
Callbacks funcionam bem.
Boa sorte!.
id
registro de data e hora baseado em Unix. Eu fiz isso por dentro before_create
. Funciona bem.
Para Rails 3, a maneira mais simples de fazer isso é usar new
com o without_protection
refinamento e então save
:
Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save
Para os dados iniciais, pode fazer sentido ignorar a validação, que você pode fazer assim:
Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save(validate: false)
Na verdade, adicionamos um método auxiliar a ActiveRecord :: Base que é declarado imediatamente antes de executar os arquivos de semente:
class ActiveRecord::Base
def self.seed_create(attributes)
new(attributes, without_protection: true).save(validate: false)
end
end
E agora:
Post.seed_create(:id => 10, :title => 'Test')
Para Rails 4, você deve usar StrongParams em vez de atributos protegidos. Nesse caso, você simplesmente poderá atribuir e salvar sem passar nenhum sinalizador para new
:
Post.new(id: 10, title: 'Test').save # optionally pass `{validate: false}`
{}
como a resposta de Samuel acima (Rails3).
id
ainda tem 10.
id
foi passado como 10 - então é exatamente o que deveria ser. Se não era isso que você esperava, pode esclarecer com um exemplo?