Existe-t-il un moyen de remplacer la valeur d'identifiant 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.
Existe-t-il un moyen de remplacer la valeur d'identifiant 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.
Réponses:
id est juste attr_protected, c'est pourquoi vous ne pouvez pas utiliser l'affectation en masse pour le définir. Cependant, lors du réglage manuel, cela fonctionne simplement:
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 de modèles ActiveHash en ActiveRecord. ActiveHash vous permet d'utiliser la même sémantique appartient_to dans 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 de la base de données référencent les identifiants en mémoire dans le fichier 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 garder toutes les références de clé étrangère identiques.
ActiveRecord::VERSION::STRING == "3.2.11"
ici (avec l'adaptateur sqlite3) et ce qui précède fonctionne pour moi.
Essayer
a_post = Post.new do |p|
p.id = 10
p.title = 'Test'
p.save
end
cela devrait vous donner ce que vous recherchez.
Vous pouvez également utiliser quelque chose comme ceci:
Post.create({:id => 10, :title => 'Test'}, :without_protection => true)
Bien que comme indiqué dans la documentation , cela contournera la sécurité des affectations de masse.
Pour les rails 4:
Post.create(:title => 'Test').update_column(:id, 10)
Other Rails 4 réponses n'a pas fonctionné pour moi. Beaucoup d'entre eux semblaient 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, attribuer un id
numéro d'identification à incrémentation automatique ne fonctionne pas 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 changez create
pour utiliser new
+, save
vous aurez toujours ce problème. Changer manuellement les éléments id
similaires p.id = 10
produit é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 qui peut discrètement corrompre votre base de données de production tout en indiquant qu'elle fonctionne.
Post.new.update(id: 10, title: 'Test')
En fait, il s'avère que faire les travaux suivants:
p = Post.new(:id => 10, :title => 'Test')
p.save(false)
validate: false
, au lieu de simplement false
. Cependant, vous rencontrez toujours le problème des attributs protégés - il existe un moyen distinct de contourner ce que j'ai décrit dans ma réponse.
Comme Jeff le souligne, 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. Faites attention 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)
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 très bien si vous devez remplir une table avec un ensemble fixe d'identifiants (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 (afin 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
Placez cette fonction create_with_id en haut de votre seeds.rb, puis utilisez-la 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 utilisez-le 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 était nécessaire de remplacer le id
avec 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
Puis :
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 très bien.
Bonne chance!.
id
horodatage basé sur Unix. J'ai fait ça à l'intérieur before_create
. Fonctionne très bien.
Pour Rails 3, le moyen le 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, ce 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 devriez utiliser StrongParams au lieu d'attributs protégés. Si tel est le cas, vous pourrez simplement attribuer et enregistrer sans passer aucun drapeau à new
:
Post.new(id: 10, title: 'Test').save # optionally pass `{validate: false}`
{}
la réponse de Samuel ci-dessus (Rails3).
id
a toujours 10 ans.
id
été passé à 10 - c'est donc exactement ce qu'il devrait être. Si ce n'est pas ce à quoi vous vous attendiez, pouvez-vous clarifier avec un exemple?
vous pouvez insérer l'identifiant 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