2010-05-12 6 views
2

MySQL a une très belle option pour l'instruction INSERT, ce qui est particulièrement utile pour les tables jointes sans la colonne id. Il insère un enregistrement, mais, au lieu de lancer une erreur si sa clé est en conflit avec la clé existante, cet enregistrement est mis à jour. Voici un exemple:Comment mettre à jour un enregistrement existant si celui en cours d'enregistrement a la même clé?

INSERT INTO table (key1,key2,data) VALUES (1,2,3) 
    ON DUPLICATE KEY UPDATE data=3; 

Comment réaliser la même chose avec Active Record? Le code résultant serait alors ressembler à ceci:

class Model < ActiveRecord::Base 
    belongs_to :key1 
    belongs_to :key2 
end 

record = Model.new 
record.key1 = key1 
record.key2 = key2 
record.data = 'new data' 
record.WHAT? #Inserts or updates `data` for the existing record 

Répondre

3

Le answer by j est la bonne idée, mais vous voudrez probablement utiliser find_or_initialize_by pour éviter plusieurs sauvegardes sur l'objet. Par exemple.

record = Model.find_or_initialize_by_key1_and_key2 new 
record.data = 'new data' 
record.save 

par les sons de celui-ci, vous voulez aussi une validation pour vérifier que le modèle ne permet pas de double rejoint aussi:

class Model < ActiveRecord::Base 
    belongs_to :key1 
    belongs_to :key2 
    validates_uniqueness_of :key1, :scope => :key2 
end 
0

d'abord qui est une solution spécifique à MySQL, pour autant que je sais que la syntaxe ne fonctionne pas avec tous les autres serveurs SQL. Généralement appelé Upsert dans les discussions.

Je voudrais aller faire une nouvelle méthode appelée save_or_update et puis si l'enregistrement échoue, avec une exception de clé en double, charger et update_params.

+0

Votre solution n'est pas très performante. Lorsque 'save' échoue, il peut également annuler une transaction de taille notable et les données doivent être rechargées. Quant à MySQL, désolé, corrigé la question. –

+0

'find' par lui-même retournera' AR :: RecordNotFound', tandis que 'find_by_ *' retournera 'nil'. – theIV

2

Vous pouvez effectuer les opérations suivantes:

record = Model.find_or_create_by_key1_and_key2(:key1 => key1, :key2 => key2) 
record.update_attribute(:data, "new data") 

EDIT

Zaïus idée semble mieux, vous devez utiliser find_or_initialize_by pour éviter de multiples arrêts, comme il a dit:]

+0

Ooh, c'est gentil! J'ai trouvé de la documentation sur [DynamicFinderMatch # new] (http://railsapi.com/doc/rails-v2.3.5/classes/ActiveRecord/DynamicFinderMatch.html#M001202), cliquez simplement sur "Show Source". – gaqzi

+0

Juste un commentaire: ce conseil semble être plus profond qu'il pourrait sembler en raison de la façon dont il est présenté ... –

+0

Je n'ai pas compris votre commentaire ... –

Questions connexes