Archives mensuelles : septembre 2016

Ruby On Rails – utiliser first_or_create

Lorsque vous utilisez ActiveRecord, vous vous intéressez forcément tôt ou tard à la fonction first_or_create et en général, vous l’utiliser comme suit:


MonJoliModele.first_or_create(attribut: "unevaleur")

Ce qu’on pense faire…

En procédant ainsi, vous pensez « Soit un enregistrement avec comme valeur du champ attribut « unevaleur » est trouvé, soit je vais en créer un ». Et bien non ! Ouvrez-vous un rails console et regardez ce qui se passe en réalité. Et oui, Rails prend le premier enregistrement de la table liée au modèle, sans même tenir compte de la valeur du champ « attribut » que vous lui donnez, pensant logiquement qu’il allait en faire quelque chose…Évidemment si votre table est vide alors Rails créera cet enregistrement avec la valeur passée en paramètre, mais c’est loin d’être tout le temps le cas.

Développeur sceptique: ne me dites pas que vous n'avez jamais fait cette tête, vous mentiriez !

Développeur sceptique: ne me dites pas que vous n’avez jamais fait cette tête, vous mentiriez !


Ce que vous vouliez faire en réalité, c’était:

MonJoliModele.where(attribut: "unevaleur").first_or_create(attribut: "unevaleur")

ou, plus élégant :

critere = {attribut: "unevaleur"}
MonJoliModele.where(critere).first_or_create(critere)

Attention donc aux effets de bord possiblement dus à la mauvaise utilisation de cette fonction. Notez qu’on peut utiliser un bloc aussi. Mais attention, il n’est exécuté qu’en cas de création d’un nouvel enregistrement et vous l’utiliserez évidemment si vous avez de nombreuses affectations à y faire, et pas forcément deux comme dans mon exemple:


critere = {attribut: "unevaleur"}
MonJoliModele.where(critere).first_or_create do |modele|
modele.autreattribut = "une autre valeur"
modele.encoreunautreattribut = "encore une autre valeur"
end

Voilà donc pour ce crash course sur l’utilisation de first_or_create, fonction dont le nom assez ambigu peut être source de confusion et qui en même temps nous épargne d’avoir à faire un find_by suivi d’un create, ce qui n’est ni très esthétique, ni très performant !