it-swarm.com.de

Überschreiben der ID beim Erstellen in ActiveRecord

Gibt es eine Möglichkeit, den ID-Wert eines Modells beim Erstellen zu überschreiben? Etwas wie:

Post.create(:id => 10, :title => 'Test')

wäre ideal, funktioniert aber offensichtlich nicht.

103
Codebeef

id ist nur attr_protected, weshalb Sie die Massenzuweisung nicht verwenden können, um sie festzulegen. Wenn Sie es jedoch manuell einstellen, funktioniert es einfach:

o = SomeObject.new
o.id = 8888
o.save!
o.reload.id # => 8888

Ich bin nicht sicher, was die ursprüngliche Motivation war, aber ich mache dies, wenn ich ActiveHash-Modelle in ActiveRecord konvertiere. Mit ActiveHash können Sie dieselbe Gehör-zu-Semantik in ActiveRecord verwenden. Statt jedoch zu migrieren und eine Tabelle zu erstellen und bei jedem Aufruf den Aufwand für die Datenbank zu verursachen, speichern Sie Ihre Daten einfach in yml-Dateien. Die Fremdschlüssel in der Datenbank verweisen auf die speicherinternen IDs im yml.

ActiveHash eignet sich hervorragend für Auswahllisten und kleine Tabellen, die sich nur selten und nur von Entwicklern ändern. Wenn Sie von ActiveHash zu ActiveRecord wechseln, ist es am einfachsten, alle Fremdschlüsselverweise gleich zu halten.

117
Jeff Dean

Sie könnten auch so etwas verwenden:

Post.create({:id => 10, :title => 'Test'}, :without_protection => true)

Obwohl dies, wie in docs angegeben, die Massenzuweisungssicherheit umgeht.

29
Samuel Heaney

Versuchen

a_post = Post.new do |p| 
  p.id = 10
  p.title = 'Test'
  p.save
end

das sollte dir geben, wonach du suchst.

29
PJ Davis

Für Rails 4:

Post.create(:title => 'Test').update_column(:id, 10)

Andere Rails 4 Antworten haben nicht bei mir funktioniert. Viele von ihnen schienen sich zu ändern Bei der Überprüfung mit der Rails Console, aber als ich die Werte in der MySQL-Datenbank überprüfte, blieben sie unverändert. Andere Antworten funktionierten nur manchmal.

Zumindest für MySQL funktioniert das Zuweisen eines id unterhalb der ID-Nummer für die automatische Inkrementierung nicht, es sei denn, Sie verwenden update_column. Beispielsweise,

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

Wenn Sie create ändern, um new + save zu verwenden, wird dieses Problem weiterhin bestehen. Manuelles Ändern von id wie p.id = 10 erzeugt auch dieses Problem.

Im Allgemeinen würde ich update_column, um id zu ändern, obwohl es eine zusätzliche Datenbankabfrage kostet, da es die ganze Zeit funktionieren wird. Dies ist ein Fehler, der möglicherweise nicht in Ihrer Entwicklungsumgebung angezeigt wird, Ihre Produktionsdatenbank jedoch unbemerkt beschädigen kann, wenn Sie sagen, dass sie funktioniert.

20
Rick Smith

Wie Jeff betont, verhält sich id so, als wäre es attr_protected. Um dies zu verhindern, müssen Sie die Liste der standardmäßig geschützten Attribute überschreiben. Gehen Sie dabei überall vorsichtig vor, damit Attributinformationen von außen kommen können. Das ID-Feld ist aus einem bestimmten Grund standardmäßig geschützt.

class Post < ActiveRecord::Base

  private

  def attributes_protected_by_default
    []
  end
end

(Getestet mit ActiveRecord 2.3.5)

6
Nic Benders

Tatsächlich stellt sich heraus, dass das Folgende funktioniert:

p = Post.new(:id => 10, :title => 'Test')
p.save(false)
6
Codebeef

wir können attributes_protected_by_default überschreiben

class Example < ActiveRecord::Base
    def self.attributes_protected_by_default
        # default is ["id", "type"]
        ["type"]
    end
end

e = Example.new(:id => 10000)
6
user510319
Post.create!(:title => "Test") { |t| t.id = 10 }

Das kommt mir nicht so vor, wie Sie es normalerweise tun würden, aber es funktioniert ganz gut, wenn Sie eine Tabelle mit einem festen Satz von IDs füllen müssen (zum Beispiel, wenn Sie mit einer Rechenaufgabe Standardwerte erstellen) und Sie das automatische Inkrementieren überschreiben möchten (damit die Tabelle bei jeder Ausführung der Aufgabe mit denselben IDs gefüllt wird):

post_types.each_with_index do |post_type|
  PostType.create!(:name => post_type) { |t| t.id = i + 1 }
end
5
Sean Cameron

Stell Dies create_with_id Funktionieren Sie oben in Ihrer seeds.rb und verwenden Sie sie dann, um Objekte zu erstellen, bei denen explizite IDs gewünscht werden.

def create_with_id(clazz, params)
obj = clazz.send(:new, params)
obj.id = params[:id]
obj.save!
    obj
end

und benutze es so

create_with_id( Foo, {id:1,name:"My Foo",prop:"My other property"})

anstatt zu verwenden

Foo.create({id:1,name:"My Foo",prop:"My other property"})

2
Darren Hicks

In diesem Fall handelte es sich um ein ähnliches Problem, bei dem das id mit einem benutzerdefinierten Datum überschrieben werden musste:

# 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

Und dann :

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">

Rückrufe funktioniert gut.

Viel Glück!.

1

In Rails 4.2.1 mit Postgresql 9.5.3, Post.create(:id => 10, :title => 'Test') funktioniert, solange noch keine Zeile mit id = 10 vorhanden ist.

0
Nikola

Für Rails 3 ist der einfachste Weg dies zu tun, new mit dem without_protection Verfeinerung und dann save:

Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save

Für Seed-Daten kann es sinnvoll sein, die Validierung zu umgehen, was Sie folgendermaßen tun können:

Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save(validate: false)

Wir haben ActiveRecord :: Base eine Hilfsmethode hinzugefügt, die unmittelbar vor dem Ausführen von Seed-Dateien deklariert wird:

class ActiveRecord::Base
  def self.seed_create(attributes)
    new(attributes, without_protection: true).save(validate: false)
  end
end

Und nun:

Post.seed_create(:id => 10, :title => 'Test')

Für Rails 4 sollten Sie StrongParams anstelle von geschützten Attributen verwenden. Wenn dies der Fall ist, können Sie einfach zuweisen und speichern, ohne dass Flags an new übergeben werden. :

Post.new(id: 10, title: 'Test').save      # optionally pass `{validate: false}`
0
PinnyM

sie können ID von SQL einfügen:

  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
0
wxianfeng