Existe-t-il un moyen de remplacer la valeur d'ID d'un modèle lors de la création? Quelque chose comme:
Post.create(:id => 10, :title => 'Test')
serait idéal, mais ne fonctionnera évidemment pas.
id est juste attr_protected, c'est pourquoi vous ne pouvez pas utiliser l'affectation en masse pour le définir. Cependant, lors de la configuration manuelle, cela fonctionne:
o = SomeObject.new
o.id = 8888
o.save!
o.reload.id # => 8888
Je ne sais pas quelle était la motivation d'origine, mais je le fais lors de la conversion des modèles ActiveHash en ActiveRecord. ActiveHash vous permet d'utiliser la même sémantique d'appartenance à ActiveRecord, mais au lieu d'avoir une migration et de créer une table, et d'encourir la surcharge de la base de données à chaque appel, vous stockez simplement vos données dans des fichiers yml. Les clés étrangères dans la base de données font référence aux identifiants en mémoire dans le yml.
ActiveHash est idéal pour les listes de sélection et les petites tables qui changent rarement et ne changent que par les développeurs. Ainsi, lorsque vous passez d'ActiveHash à ActiveRecord, il est plus simple de conserver toutes les références de clés étrangères de la même manière.
Vous pouvez également utiliser quelque chose comme ceci:
Post.create({:id => 10, :title => 'Test'}, :without_protection => true)
Bien que comme indiqué dans le docs , cela contourne la sécurité d'affectation de masse.
Essayer
a_post = Post.new do |p|
p.id = 10
p.title = 'Test'
p.save
end
cela devrait vous donner ce que vous cherchez.
Pour Rails 4:
Post.create(:title => 'Test').update_column(:id, 10)
Autres Rails 4 réponses pas ont fonctionné pour moi. Beaucoup d'entre elles sont apparues pour changer lors de la vérification à l'aide de la console Rails, mais lorsque j'ai vérifié les valeurs dans la base de données MySQL, elles sont restées inchangées. D'autres réponses ne fonctionnaient que parfois.
Pour MySQL au moins, l'attribution d'un id
en dessous du numéro d'identification de l'incrémentation automatique ne pas fonctionne sauf si vous utilisez update_column
. Par exemple,
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 vous modifiez create
pour utiliser new
+ save
, vous aurez toujours ce problème. Changer manuellement le id
comme p.id = 10
génère également ce problème.
En général, j'utiliserais update_column
pour changer le id
même si cela coûte une requête de base de données supplémentaire car cela fonctionnera tout le temps. Il s'agit d'une erreur qui peut ne pas apparaître dans votre environnement de développement, mais peut corrompre discrètement votre base de données de production tout en disant qu'elle fonctionne.
Comme le souligne Jeff, id se comporte comme s'il était attr_protected. Pour éviter cela, vous devez remplacer la liste des attributs protégés par défaut. Soyez prudent en faisant cela partout où les informations d'attribut peuvent provenir de l'extérieur. Le champ id est protégé par défaut pour une raison.
class Post < ActiveRecord::Base
private
def attributes_protected_by_default
[]
end
end
(Testé avec ActiveRecord 2.3.5)
En fait, il s'avère que faire les travaux suivants:
p = Post.new(:id => 10, :title => 'Test')
p.save(false)
nous pouvons remplacer 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 }
Cela ne me semble pas être le genre de chose que vous voudriez normalement faire, mais cela fonctionne assez bien si vous devez remplir une table avec un ensemble fixe d'ID (par exemple lors de la création de valeurs par défaut à l'aide d'une tâche de râteau) et vous souhaitez remplacer l'incrémentation automatique (de sorte que chaque fois que vous exécutez la tâche, la table soit remplie avec les mêmes identifiants):
post_types.each_with_index do |post_type|
PostType.create!(:name => post_type) { |t| t.id = i + 1 }
end
Mettez ça create_with_id fonction en haut de votre seeds.rb, puis utilisez-le pour créer votre objet là où des identifiants explicites sont souhaités.
def create_with_id(clazz, params)
obj = clazz.send(:new, params)
obj.id = params[:id]
obj.save!
obj
end
et l'utiliser comme ça
create_with_id( Foo, {id:1,name:"My Foo",prop:"My other property"})
à la place d'utiliser
Foo.create({id:1,name:"My Foo",prop:"My other property"})
Ce cas est un problème similaire qui devait remplacer le id
par une sorte de date personnalisée:
# 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
Et alors :
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">
Les rappels fonctionnent bien.
Bonne chance!.
Dans Rails 4.2.1 avec Postgresql 9.5.3, Post.create(:id => 10, :title => 'Test')
fonctionne tant qu'il n'y a pas déjà une ligne avec id = 10.
Pour Rails 3, la façon la plus simple de le faire est d'utiliser new
avec le without_protection
raffinement, puis save
:
Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save
Pour les données de départ, il peut être judicieux de contourner la validation que vous pouvez faire comme ceci:
Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save(validate: false)
Nous avons en fait ajouté une méthode d'assistance à ActiveRecord :: Base qui est déclarée immédiatement avant l'exécution des fichiers de départ:
class ActiveRecord::Base
def self.seed_create(attributes)
new(attributes, without_protection: true).save(validate: false)
end
end
Et maintenant:
Post.seed_create(:id => 10, :title => 'Test')
Pour Rails 4, vous devez utiliser StrongParams au lieu d'attributs protégés. Si tel est le cas, vous pourrez simplement attribuer et enregistrer sans passer aucun indicateur à new
:
Post.new(id: 10, title: 'Test').save # optionally pass `{validate: false}`
vous pouvez insérer id par 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