Rails: mettre à jour l'attribut du modèle sans appeler les rappels
J'ai un modèle utilisateur qui a un attribut: credits. Je veux un bouton simple qui ajoutera 5 aux crédits de l'utilisateur, à travers une route appelée "add" de sorte que /users/3/add ajouterait 5 aux crédits de user id = 3.
def add
@user = User.find(params[:id])
@user.credits += 5
redirect_to root_path
end
C'est la partie pertinente de mon contrôleur. Le problème est, je ne veux pas appeler @user.enregistrer parce que j'ai un rappel before_save Qui re-crypte le mot de passe de l'utilisateur en fonction de l'heure UTC actuelle. Je veux juste simplement ajouter 5 à l'attribut et éviter le rappel, je n'ai jamais pensé qu'une chose aussi simple pourrait être si difficile.
Modifier:
J'ai changé le rappel en: before_create, voici mon nouveau code de contrôleur (partie):
def add
@user = User.find(params[:id])
@user.add_credits(5)
@user.save
flash[:success] = "Credits added!"
redirect_to root_path
end
Et voici mon code dans le modèle:
def add_credits(num)
self.credits = num
end
Édition 2:
Ok C'était un problème de validation qui a fait que les changements dans "EDIT" ne fonctionnaient pas, mais j'aimerais toujours une réponse à la question initiale de la mise à jour sans rappels!
9 réponses
Rails 3.1 introduit update_column
, qui est le même que update_attribute
, mais sans déclencher de validations ou de rappels:
Http://apidock.com/rails/ActiveRecord/Persistence/update_column
Pour mettre à jour plusieurs attributs sans rappels, vous pouvez utiliser update_all dans votre modèle comme suit:
self.class.update_all({name: value, name: value}, self.class.primary_key => id)
Si vous voulez vraiment, vous pouvez même essayer même une méthode update_columns et mélanger ceci à votre classe de base d'enregistrement actif.
Pour mettre à jour un attribut, vous pouvez utiliser update_column. En outre, il existe des méthodes spécifiques qui peuvent être trouvées dans les rails guides http://guides.rubyonrails.org/active_record_callbacks.html#skipping-callbacks
Je pense que vous devriez utiliser la méthode update_counters dans ce cas. Utilisez-le comme ceci dans votre action de contrôleur:
def add
User.update_counters params[:id], :credits => 5
redirect_to root_path
end
En réponse générale, dans Rails 4, c'est un moyen simple de mettre à jour les attributs sans déclencher de rappels:
@user.update_column 'credits', 5
Si vous devez mettre à jour plusieurs attributs sans déclencher des rappels:
@user.update_columns credits: 5, bankrupt: false
Il y a d'autres options ici dans les Rails Guides si vous préférez, mais j'ai trouvé cette façon d'être le plus facile.
Vous devriez pouvoir utiliser update_all pour éviter de déclencher des rappels.
def add
@user = User.find(params[:id])
User.where(:id=>@user.id).update_all(:credits => @user.credits+5)
redirect_to root_path
end
Je préfère mettre cette logique dans le modèle, mais cela devrait fonctionner pour résoudre votre problème d'origine comme spécifié dans le contrôleur.
Quelques options pour faire cela dans rails4 http://edgeguides.rubyonrails.org/active_record_callbacks.html#skipping-callbacks
Pour mongoid, j'ai fini par utiliser http://mongoid.org/en/mongoid/docs/persistence.html Plus précisément, vous pouvez utiliser:
person.set(name:"Robert Pulson")
Et aucun rappel ne sera émis. Tellement cool.
Peut-être que votre autre crochet before_save devrait vérifier si le mot de passe de l'Utilisateur a réellement changé avant de le chiffrer à nouveau.
Vous avez un certain nombre d'options, y compris la modification du rappel que vous utilisez, par exemple, after_create
.
Vous pouvez mettre à jour des colonnes sans déclencher de rappels, voir sauter les rappels dans le guide AR. Par exemple, update_column
ne déclenche pas de rappels. Le lien précédent répertorie les fonctions non déclenchantes.
Vous pouvez également utiliser l'un des formulaires de rappel conditionnel (ou même un observateur) pour savoir quand le mot de passe est modifié. Voir Activerecord::Sale, par exemple, @user.password_changed?
.