2011-01-14 13 views
19

Hat ActiveRecord eine integrierte Upsert-Funktionalität? Ich weiß, ich könnte es selbst schreiben, aber ich will natürlich nicht, wenn so etwas schon existiert.Upsert in Rails ActiveRecord

Antwort

13

Model.find_or_initialize wahrscheinlich macht was Sie wollen. Sie können es mit save oder update_attributes ketten, wenn das sinnvoll ist.

Weitere Informationen in der Rails Guides.

+3

siehe Kommentar zu Pasta's Antwort – tybro0103

+1

Hat jemand gesehen, dass dies einen Upsert generiert? Der Schienenführer zeigt an, dass das neue Objekt noch nicht in der DB gespeichert ist, so dass ich nicht sehen kann, wie dies ein echtes DB-Upsert ist. Das heißt, wird in einer Multithread-Umgebung nicht zuverlässig funktionieren. – stuckj

+2

Diese Lösung weist Nebenläufigkeitsprobleme auf. Es wird fehlschlagen, wenn ein anderer Thread die Tabelle zwischen 'find_or_initialize' und' save' aktualisiert. –

3

Es ist Model.find_or_create auch

+6

Dies ist kein Upsert. Es macht eine Auswahl und dann (optional) eine Einfügung. Während Sie in einer Single-Thread-Welt in einer Multi-Thread-Welt den gleichen Effekt erzielen, benötigen Sie einen tatsächlichen Upsert. – tybro0103

-1

ich auf einen Blog-Post geschrieben hatte, Wie können wir das erreichen? Schau es Dir an here.

Sie müssen eine aktive Datensatzerweiterung schreiben. Es wird ungefähr so ​​aussehen.

module ActiveRecordExtension 
    extend ActiveSupport::Concern 

    def self.upsert(attributes) 
    begin 
     create(attributes) 
    rescue ActiveRecord::RecordNotUnique, PG::UniqueViolation => e 
     find_by_primary_key(attributes['primary_key']). 
     update(attributes) 
    end 
    end 
end 

ActiveRecord::Base.send(:include, ActiveRecordExtension) 
+3

Kein Fan von Fehlern, um den erwarteten logischen Fluss zu steuern, insbesondere wenn Sie vorhersagen können, wann dieser Fehler auftritt. – kmanzana

+0

guten Punkt @kmanzana. irgendwie zu hackisch für mich. meiner bescheidenen Meinung nach – olleh

0

Der IMO Upsert-Mechanismus erfordert eine benutzerdefinierte Konfiguration für jedes Modell.

Die beste Lösung wäre also, eine benutzerdefinierte SQL-Abfrage für ein Modell zu implementieren, z.

insert into <table> (<field_1>, ..., <field_n>) 
    values (<field_1_value>, ..., <field_n_value>) 
on duplicate key update 
    field_x = field_x_value, 
    ... 
    field_z = field_z_value;